@@ -1768,7 +1768,16 @@ def test_detect_pip_usage_in_repl(self):
1768
1768
" outside of the Python REPL"
1769
1769
)
1770
1770
self .assertIn (hint , output )
1771
+
1771
1772
class TestPyReplCtrlD (TestCase ):
1773
+ """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior.
1774
+
1775
+ Ctrl+D should:
1776
+ - Exit on empty buffer
1777
+ - Delete character when cursor is in middle of line
1778
+ - Perform no operation when cursor is at end of line without newline
1779
+ - Exit multiline mode when cursor is at end with trailing newline
1780
+ """
1772
1781
def prepare_reader (self , events ):
1773
1782
console = FakeConsole (events )
1774
1783
config = ReadlineConfig (readline_completer = None )
@@ -1784,17 +1793,44 @@ def test_ctrl_d_empty_buffer(self):
1784
1793
with self .assertRaises (EOFError ):
1785
1794
multiline_input (reader )
1786
1795
1787
- def test_ctrl_d_multiline_mode (self ):
1788
- """Test that pressing Ctrl+D in multiline mode exits multiline mode"""
1796
+ def test_ctrl_d_multiline_with_new_line (self ):
1797
+ """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode"""
1789
1798
events = itertools .chain (
1790
- code_to_events ("def f():\n " ), # Enter multiline mode
1799
+ code_to_events ("def f():\n pass \n " ), # Enter multiline mode with trailing newline
1791
1800
[
1792
1801
Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1793
1802
],
1794
1803
)
1795
- reader = self .prepare_reader (events )
1796
- output = multiline_input (reader )
1797
- self .assertEqual (output , "def f():\n " ) # Should return current input
1804
+ reader , _ = handle_all_events (events )
1805
+ self .assertTrue (reader .finished )
1806
+ self .assertEqual ("def f():\n pass\n " , "" .join (reader .buffer ))
1807
+
1808
+ def test_ctrl_d_multiline_middle_of_line (self ):
1809
+ """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character"""
1810
+ events = itertools .chain (
1811
+ code_to_events ("def f():\n hello world" ), # Enter multiline mode
1812
+ [
1813
+ Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))
1814
+ ] * 5 , # move cursor to 'w' in "world"
1815
+ [
1816
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1817
+ ], # Ctrl+D should delete 'w'
1818
+ )
1819
+ reader , _ = handle_all_events (events )
1820
+ self .assertFalse (reader .finished )
1821
+ self .assertEqual ("def f():\n hello orld" , "" .join (reader .buffer ))
1822
+
1823
+ def test_ctrl_d_multiline_end_of_line_no_newline (self ):
1824
+ """Test that pressing Ctrl+D at end of line without newline performs no operation"""
1825
+ events = itertools .chain (
1826
+ code_to_events ("def f():\n hello" ), # Enter multiline mode, no trailing newline
1827
+ [
1828
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1829
+ ], # Ctrl+D should be no-op
1830
+ )
1831
+ reader , _ = handle_all_events (events )
1832
+ self .assertFalse (reader .finished )
1833
+ self .assertEqual ("def f():\n hello" , "" .join (reader .buffer ))
1798
1834
1799
1835
def test_ctrl_d_single_line (self ):
1800
1836
"""Test that pressing Ctrl+D in single line mode deletes current character"""
0 commit comments