-
-
Notifications
You must be signed in to change notification settings - Fork 33k
gh-139352: prevent altering PYTHON_HISTORY
file when running REPL tests
#139380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ef22044
244cd0b
70ba65a
41ceb15
a574767
e44596e
5126bf0
6237cc7
9d31898
1c7e2d9
21b3083
8939460
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,8 @@ | |
from .unix_console import UnixConsole as Console, _error | ||
|
||
ENCODING = sys.getdefaultencoding() or "latin1" | ||
_EDITLINE_MARKER = "_HiStOrY_V2_" | ||
_EDITLINE_BYTES_MARKER = b"_HiStOrY_V2_" | ||
|
||
|
||
# types | ||
|
@@ -60,7 +62,7 @@ | |
TYPE_CHECKING = False | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Mapping | ||
from typing import Any, IO, Mapping | ||
|
||
|
||
MoreLinesCallable = Callable[[str], bool] | ||
|
@@ -425,6 +427,15 @@ def set_history_length(self, length: int) -> None: | |
def get_current_history_length(self) -> int: | ||
return len(self.get_reader().history) | ||
|
||
@staticmethod | ||
def _is_editline_history(filename: str | IO[bytes]) -> bool: | ||
if isinstance(filename, str): | ||
if not os.path.exists(filename): | ||
return False | ||
with open(filename, "rb") as f: | ||
return f.readline().startswith(_EDITLINE_BYTES_MARKER) | ||
return filename.readline().startswith(_EDITLINE_BYTES_MARKER) | ||
|
||
def read_history_file(self, filename: str = gethistoryfile()) -> None: | ||
# multiline extension (really a hack) for the end of lines that | ||
# are actually continuations inside a single multiline_input() | ||
|
@@ -433,8 +444,7 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None: | |
history = self.get_reader().history | ||
|
||
with open(os.path.expanduser(filename), 'rb') as f: | ||
is_editline = f.readline().startswith(b"_HiStOrY_V2_") | ||
if is_editline: | ||
if self._is_editline_history(f): | ||
encoding = "unicode-escape" | ||
else: | ||
f.seek(0) | ||
|
@@ -457,9 +467,12 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None: | |
def write_history_file(self, filename: str = gethistoryfile()) -> None: | ||
maxlength = self.saved_history_length | ||
history = self.get_reader().get_trimmed_history(maxlength) | ||
f = open(os.path.expanduser(filename), "w", | ||
encoding="utf-8", newline="\n") | ||
with f: | ||
|
||
filename = os.path.expanduser(filename) | ||
is_editline = self._is_editline_history(filename) | ||
with open(filename, "w", encoding="utf-8", newline="\n") as f: | ||
if is_editline: | ||
f.write(f"{_EDITLINE_MARKER}\n") | ||
Comment on lines
+474
to
+475
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect and this change is irrelevant to the bug you're fixing. The editline format is somewhat different, a file written by us will not be readable by editline anyway, and now you'll make pyrepl mangle unicode in its own history until you delete the history file. |
||
for entry in history: | ||
entry = entry.replace("\n", "\r\n") # multiline history support | ||
f.write(entry + "\n") | ||
|
@@ -469,9 +482,9 @@ def append_history_file(self, filename: str = gethistoryfile()) -> None: | |
saved_length = self.get_history_length() | ||
length = self.get_current_history_length() - saved_length | ||
history = reader.get_trimmed_history(length) | ||
f = open(os.path.expanduser(filename), "a", | ||
encoding="utf-8", newline="\n") | ||
with f: | ||
|
||
filename = os.path.expanduser(filename) | ||
with open(filename, "a", encoding="utf-8", newline="\n") as f: | ||
for entry in history: | ||
entry = entry.replace("\n", "\r\n") # multiline history support | ||
f.write(entry + "\n") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
import random | ||
import re | ||
import shlex | ||
import stat | ||
import sys | ||
import sysconfig | ||
import time | ||
|
@@ -160,6 +161,13 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): | |
self.next_single_test: TestName | None = None | ||
self.next_single_filename: StrPath | None = None | ||
|
||
history_file = os.path.join(os.path.expanduser('~'), '.python_history') | ||
self.__history_file = history_file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: don't use double underscores. We don't use them anywhere in libregrtest. A single underscore's better. |
||
self.__history_stat: tuple[int, int] | None = None | ||
if os.path.exists(history_file): | ||
st = os.stat(history_file) | ||
self.__history_stat = (stat.S_IFMT(st.st_mode), st.st_size) | ||
|
||
def log(self, line: str = '') -> None: | ||
self.logger.log(line) | ||
|
||
|
@@ -392,6 +400,16 @@ def run_test( | |
else: | ||
result = run_single_test(test_name, runtests) | ||
|
||
if self.__history_stat is None: | ||
if os.path.exists(self.__history_file): | ||
raise AssertionError(f"{test_name}: created history file") | ||
else: | ||
if not os.path.exists(self.__history_file): | ||
raise AssertionError(f"{test_name}: deleted history file") | ||
st = os.stat(self.__history_file) | ||
if self.__history_stat != (stat.S_IFMT(st.st_mode), st.st_size): | ||
raise AssertionError(f"{test_name}: altered history file") | ||
|
||
self.results.accumulate_result(result, runtests) | ||
|
||
return result | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Prevent altering the content of the :envvar:`PYTHON_HISTORY` file after | ||
running REPL tests. Patch by Bénédikt Tran. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't implement the
str
bit. Now there's two functions in one.