Skip to content

Commit 52d3d1b

Browse files
miss-islingtonedvilmeblurb-it[bot]pablogsal
authored
[3.15] gh-159225: PyREPL input should not write to history (GH-159263) (#159369)
gh-159225: PyREPL input should not write to history (GH-159263) * gh-159225: PyREPL input should not write to history Values entered in an input statement are not Python statements and thus the reader should suspend history recording when in input mode * 📜🤖 Added by blurb_it. * Remove comment * Only suspend writing * gh-159225: Cover input history isolation and interrupted reads --------- (cherry picked from commit 6f92424) Co-authored-by: Eduardo Villalpando Mello <eduardovil@microsoft.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent ef498e0 commit 52d3d1b

4 files changed

Lines changed: 67 additions & 2 deletions

File tree

‎Lib/_pyrepl/historical_reader.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,14 @@ def suspend(self) -> SimpleContextManager:
294294

295295
@contextmanager
296296
def suspend_history(self) -> SimpleContextManager:
297+
with self.suspend_history_writes():
298+
del self.history[:]
299+
yield
300+
301+
@contextmanager
302+
def suspend_history_writes(self) -> SimpleContextManager:
297303
try:
298304
old_history = self.history[:]
299-
del self.history[:]
300305
yield
301306
finally:
302307
self.history[:] = old_history

‎Lib/_pyrepl/readline.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ def input(self, prompt: object = "") -> str:
385385
prompt_str = str(prompt)
386386
reader.ps1 = prompt_str
387387
sys.audit("builtins.input", prompt_str)
388-
result = reader.readline(startup_hook=self.startup_hook)
388+
with reader.suspend_history_writes():
389+
result = reader.readline(startup_hook=self.startup_hook)
389390
sys.audit("builtins.input/result", result)
390391
return result
391392

‎Lib/test/test_pyrepl/test_pyrepl.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,64 @@ def test_get_line_buffer_returns_str(self):
690690
wrapper = _ReadlineWrapper(f_in=None, f_out=None, reader=reader)
691691
self.assertIs(type(wrapper.get_line_buffer()), str)
692692

693+
def test_input_does_not_add_history(self):
694+
for history in ([], ["previous input"]):
695+
with self.subTest(history=history):
696+
reader = self.prepare_reader(
697+
code_to_events("first input\nsecond input\n1+1\n")
698+
)
699+
reader.history[:] = history
700+
wrapper = _ReadlineWrapper(f_in=None, f_out=None, reader=reader)
701+
702+
self.assertEqual(wrapper.input(), "first input")
703+
self.assertEqual(reader.history, history)
704+
self.assertEqual(wrapper.input(), "second input")
705+
self.assertEqual(reader.history, history)
706+
self.assertEqual(multiline_input(reader), "1+1")
707+
self.assertEqual(reader.history, [*history, "1+1"])
708+
709+
def test_input_can_read_history(self):
710+
events = [
711+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
712+
Event(evt="key", data="\n", raw=bytearray(b"\n")),
713+
]
714+
reader = self.prepare_reader(events)
715+
reader.history.append("previous input")
716+
wrapper = _ReadlineWrapper(f_in=None, f_out=None, reader=reader)
717+
718+
self.assertEqual(wrapper.input(), "previous input")
719+
self.assertEqual(reader.history, ["previous input"])
720+
721+
def test_input_does_not_modify_history(self):
722+
events = itertools.chain(
723+
[Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))],
724+
code_to_events(" edited"),
725+
[Event(evt="key", data="down", raw=bytearray(b"\x1bOB"))],
726+
code_to_events("new input\n"),
727+
)
728+
reader = self.prepare_reader(events)
729+
reader.history.append("previous input")
730+
wrapper = _ReadlineWrapper(f_in=None, f_out=None, reader=reader)
731+
732+
self.assertEqual(wrapper.input(), "new input")
733+
self.assertEqual(reader.history, ["previous input"])
734+
735+
def test_input_error_does_not_modify_history(self):
736+
for text, exception in (
737+
("partial input\x03", KeyboardInterrupt),
738+
("\x04", EOFError),
739+
):
740+
with self.subTest(exception=exception):
741+
reader = self.prepare_reader(code_to_events(text + "1+1\n"))
742+
reader.history.append("previous input")
743+
wrapper = _ReadlineWrapper(f_in=None, f_out=None, reader=reader)
744+
745+
with self.assertRaises(exception):
746+
wrapper.input()
747+
self.assertEqual(reader.history, ["previous input"])
748+
self.assertEqual(multiline_input(reader), "1+1")
749+
self.assertEqual(reader.history, ["previous input", "1+1"])
750+
693751
def test_multiline_edit(self):
694752
events = itertools.chain(
695753
code_to_events("def f():\n...\n\n"),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent ``input`` calls in PyREPL from modifying readline history.

0 commit comments

Comments
 (0)