From c13de7001a627fee949a9333446387f22806bad2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 2 Jul 2024 12:18:27 +0300 Subject: [PATCH] Refactor register_readline() * make readline stuff more symmetrical wrt reading/writing * prevent truncation of the history file * add regression test Should finally address #121245. --- Lib/site.py | 28 ++++++++++++++-------------- Lib/test/test_pyrepl/test_pyrepl.py | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Lib/site.py b/Lib/site.py index daa56e158949dbd..f5caf0295180dc5 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -509,6 +509,10 @@ def register_readline(): pass if readline.get_current_history_length() == 0: + try: + from _pyrepl.main import CAN_USE_PYREPL + except ImportError: + CAN_USE_PYREPL = False # If no history was loaded, default to .python_history, # or PYTHON_HISTORY. # The guard is necessary to avoid doubling history size at @@ -516,28 +520,24 @@ def register_readline(): # through a PYTHONSTARTUP hook, see: # http://bugs.python.org/issue5845#msg198636 history = gethistoryfile() + if os.getenv("PYTHON_BASIC_REPL") or not CAN_USE_PYREPL: + my_readline = readline + else: + my_readline = _pyrepl.readline try: - if os.getenv("PYTHON_BASIC_REPL"): - readline.read_history_file(history) - else: - _pyrepl.readline.read_history_file(history) + my_readline.read_history_file(history) except (OSError,* _pyrepl.unix_console._error): pass def write_history(): try: - from _pyrepl.main import CAN_USE_PYREPL - except ImportError: - CAN_USE_PYREPL = False - - try: - if os.getenv("PYTHON_BASIC_REPL") or not CAN_USE_PYREPL: - readline.write_history_file(history) - else: - _pyrepl.readline.write_history_file(history) - except (FileNotFoundError, PermissionError): + if my_readline.get_current_history_length() > 0: + my_readline.write_history_file(history) + except (FileNotFoundError, PermissionError, + _pyrepl.unix_console.InvalidTerminal): # home directory does not exist or is not writable # https://bugs.python.org/issue19891 + # or terminal doesn't have the required capability pass atexit.register(write_history) diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index b189d3291e81812..bec240063d9062b 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -1,10 +1,12 @@ import io import itertools import os +import pathlib import rlcompleter import select import subprocess import sys +import tempfile from unittest import TestCase, skipUnless from unittest.mock import patch from test.support import force_not_colorized @@ -898,6 +900,21 @@ def test_python_basic_repl(self): self.assertNotIn("Exception", output) self.assertNotIn("Traceback", output) + def setUp(self): + self.hfile = tempfile.NamedTemporaryFile(delete=False) + + def tearDown(self): + self.hfile.close() + + def test_not_wiping_history_file(self): + env = os.environ.copy() + env.update({"PYTHON_HISTORY": self.hfile.name}) + commands = "123\nspam\nexit()\n" + output, exit_code = self.run_repl(commands, env=env) + self.assertIn("123", output) + self.assertIn("spam", output) + self.assertNotEqual(pathlib.Path(self.hfile.name).stat().st_size, 0) + def run_repl(self, repl_input: str | list[str], env: dict | None = None) -> tuple[str, int]: master_fd, slave_fd = pty.openpty() process = subprocess.Popen(