FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-127495: Append to history file after every statement in PyREPL by skirpichev · Pull Request #132294 · python/cpython · GitHub

Repository navigation

16 changes: 16 additions & 0 deletions Lib/_pyrepl/readline.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
# "set_pre_input_hook",
"set_startup_hook",
"write_history_file",
"append_history_file",
# ---- multiline extensions ----
"multiline_input",
]
Expand Down Expand Up @@ -446,6 +447,7 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None:
del buffer[:]
if line:
history.append(line)
self.set_history_length(self.get_current_history_length())

def write_history_file(self, filename: str = gethistoryfile()) -> None:
maxlength = self.saved_history_length
Expand All @@ -457,6 +459,19 @@ def write_history_file(self, filename: str = gethistoryfile()) -> None:
entry = entry.replace("\n", "\r\n") # multiline history support
f.write(entry + "\n")

def append_history_file(self, filename: str = gethistoryfile()) -> None:
reader = self.get_reader()
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:
for entry in history:
entry = entry.replace("\n", "\r\n") # multiline history support
f.write(entry + "\n")
self.set_history_length(saved_length + length)

def clear_history(self) -> None:
del self.get_reader().history[:]

Expand Down Expand Up @@ -526,6 +541,7 @@ def insert_text(self, text: str) -> None:
get_current_history_length = _wrapper.get_current_history_length
read_history_file = _wrapper.read_history_file
write_history_file = _wrapper.write_history_file
append_history_file = _wrapper.append_history_file
clear_history = _wrapper.clear_history
get_history_item = _wrapper.get_history_item
remove_history_item = _wrapper.remove_history_item
Expand Down
7 changes: 6 additions & 1 deletion Lib/_pyrepl/simple_interact.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
import os
import sys
import code
import warnings

from .readline import _get_reader, multiline_input
from .readline import _get_reader, multiline_input, append_history_file


_error: tuple[type[Exception], ...] | type[Exception]
Expand Down Expand Up @@ -144,6 +145,10 @@ def maybe_run_command(statement: str) -> bool:
input_name = f"<python-input-{input_n}>"
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
assert not more
try:
append_history_file()
except (FileNotFoundError, PermissionError, OSError) as e:
warnings.warn(f"failed to open the history file for writing: {e}")
input_n += 1
except KeyboardInterrupt:
r = _get_reader()
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_pyrepl/test_pyrepl.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def _run_repl(
else:
os.close(master_fd)
process.kill()
process.wait(timeout=SHORT_TIMEOUT)
self.fail(f"Timeout while waiting for output, got: {''.join(output)}")

os.close(master_fd)
Expand Down Expand Up @@ -1341,6 +1342,27 @@ def test_readline_history_file(self):
self.assertEqual(exit_code, 0)
self.assertNotIn("\\040", pathlib.Path(hfile.name).read_text())

def test_history_survive_crash(self):
env = os.environ.copy()
commands = "1\nexit()\n"
output, exit_code = self.run_repl(commands, env=env)
if "can't use pyrepl" in output:
self.skipTest("pyrepl not available")

with tempfile.NamedTemporaryFile() as hfile:
env["PYTHON_HISTORY"] = hfile.name
commands = "spam\nimport time\ntime.sleep(1000)\npreved\n"
try:
self.run_repl(commands, env=env)
except AssertionError:
pass

history = pathlib.Path(hfile.name).read_text()
self.assertIn("spam", history)
self.assertIn("time", history)
self.assertNotIn("sleep", history)
self.assertNotIn("preved", history)

def test_keyboard_interrupt_after_isearch(self):
output, exit_code = self.run_repl(["\x12", "\x03", "exit"])
self.assertEqual(exit_code, 0)
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
In PyREPL, append a new entry to the ``PYTHON_HISTORY`` file *after* every
statement. This should preserve command-line history after interpreter is
terminated. Patch by Sergey B Kirpichev.

Back | FazBrowse Home | New Git URL