Skip to content

Commit 16848f9

Browse files
committed
gh-133400: Fixed Ctrl+D (^D) behavior in :mod:_pyrepl module
1 parent 92337f6 commit 16848f9

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

Lib/_pyrepl/commands.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,9 @@ def do(self) -> None:
421421
r.update_screen()
422422
r.console.finish()
423423
raise EOFError
424+
elif "\n" in b and self.event[-1] == "\004":
425+
self.finish = True
426+
424427
for i in range(r.get_arg()):
425428
if r.pos != len(b):
426429
del b[r.pos]

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,3 +1610,43 @@ def test_prompt_after_help(self):
16101610
# Extra stuff (newline and `exit` rewrites) are necessary
16111611
# because of how run_repl works.
16121612
self.assertNotIn(">>> \n>>> >>>", cleaned_output)
1613+
1614+
class TestPyReplCtrlD(TestCase):
1615+
def prepare_reader(self, events):
1616+
console = FakeConsole(events)
1617+
config = ReadlineConfig(readline_completer=None)
1618+
reader = ReadlineAlikeReader(console=console, config=config)
1619+
return reader
1620+
1621+
def test_ctrl_d_empty_buffer(self):
1622+
"""Test that pressing Ctrl+D on empty buffer exits the program"""
1623+
events = [
1624+
Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D
1625+
]
1626+
reader = self.prepare_reader(events)
1627+
with self.assertRaises(EOFError):
1628+
multiline_input(reader)
1629+
1630+
def test_ctrl_d_multiline_mode(self):
1631+
"""Test that pressing Ctrl+D in multiline mode exits multiline mode"""
1632+
events = itertools.chain(
1633+
code_to_events("def f():\n"), # Enter multiline mode
1634+
[
1635+
Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D
1636+
],
1637+
)
1638+
reader = self.prepare_reader(events)
1639+
output = multiline_input(reader)
1640+
self.assertEqual(output, "def f():\n ") # Should return current input
1641+
1642+
def test_ctrl_d_single_line(self):
1643+
"""Test that pressing Ctrl+D in single line mode deletes current character"""
1644+
events = itertools.chain(
1645+
code_to_events("hello"),
1646+
[Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))], # move left
1647+
[Event(evt="key", data="\x04", raw=bytearray(b"\x04"))], # Ctrl+D
1648+
code_to_events("\n"),
1649+
)
1650+
reader = self.prepare_reader(events)
1651+
output = multiline_input(reader)
1652+
self.assertEqual(output, "hell") # Should delete the last character
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed Ctrl+D (^D) behavior in _pyrepl module: Now properly exits multiline
2+
mode when pressed in multiline section. Added test cases to verify the
3+
behavior in both multiline and single line modes.

0 commit comments

Comments
 (0)