@@ -1814,3 +1814,87 @@ def test_showrefcount(self):
1814
1814
output , _ = self .run_repl ("1\n 1+2\n exit()\n " , cmdline_args = ['-Xshowrefcount' ], env = env )
1815
1815
matches = re .findall (r'\[-?\d+ refs, \d+ blocks\]' , output )
1816
1816
self .assertEqual (len (matches ), 3 )
1817
+
1818
+
1819
+ class TestPyReplCtrlD (TestCase ):
1820
+ """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior.
1821
+
1822
+ Ctrl+D should:
1823
+ - Exit on empty buffer (raises EOFError)
1824
+ - Delete character when cursor is in middle of line
1825
+ - Perform no operation when cursor is at end of line without newline
1826
+ - Exit multiline mode when cursor is at end with trailing newline
1827
+ - Run code up to that point when pressed on blank line with preceding lines
1828
+ """
1829
+ def prepare_reader (self , events ):
1830
+ console = FakeConsole (events )
1831
+ config = ReadlineConfig (readline_completer = None )
1832
+ reader = ReadlineAlikeReader (console = console , config = config )
1833
+ return reader
1834
+
1835
+ def test_ctrl_d_empty_line (self ):
1836
+ """Test that pressing Ctrl+D on empty line exits the program"""
1837
+ events = [
1838
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1839
+ ]
1840
+ reader = self .prepare_reader (events )
1841
+ with self .assertRaises (EOFError ):
1842
+ multiline_input (reader )
1843
+
1844
+ def test_ctrl_d_multiline_with_new_line (self ):
1845
+ """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode"""
1846
+ events = itertools .chain (
1847
+ code_to_events ("def f():\n pass\n " ), # Enter multiline mode with trailing newline
1848
+ [
1849
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1850
+ ],
1851
+ )
1852
+ reader , _ = handle_all_events (events )
1853
+ self .assertTrue (reader .finished )
1854
+ self .assertEqual ("def f():\n pass\n " , "" .join (reader .buffer ))
1855
+
1856
+ def test_ctrl_d_multiline_middle_of_line (self ):
1857
+ """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character"""
1858
+ events = itertools .chain (
1859
+ code_to_events ("def f():\n hello world" ), # Enter multiline mode
1860
+ [
1861
+ Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))
1862
+ ] * 5 , # move cursor to 'w' in "world"
1863
+ [
1864
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1865
+ ], # Ctrl+D should delete 'w'
1866
+ )
1867
+ reader , _ = handle_all_events (events )
1868
+ self .assertFalse (reader .finished )
1869
+ self .assertEqual ("def f():\n hello orld" , "" .join (reader .buffer ))
1870
+
1871
+ def test_ctrl_d_multiline_end_of_line_no_newline (self ):
1872
+ """Test that pressing Ctrl+D at end of line without newline performs no operation"""
1873
+ events = itertools .chain (
1874
+ code_to_events ("def f():\n hello" ), # Enter multiline mode, no trailing newline
1875
+ [
1876
+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1877
+ ], # Ctrl+D should be no-op
1878
+ )
1879
+ reader , _ = handle_all_events (events )
1880
+ self .assertFalse (reader .finished )
1881
+ self .assertEqual ("def f():\n hello" , "" .join (reader .buffer ))
1882
+
1883
+ def test_ctrl_d_single_line_middle_of_line (self ):
1884
+ """Test that pressing Ctrl+D in single line mode deletes current character"""
1885
+ events = itertools .chain (
1886
+ code_to_events ("hello" ),
1887
+ [Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))], # move left
1888
+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1889
+ )
1890
+ reader , _ = handle_all_events (events )
1891
+ self .assertEqual ("hell" , "" .join (reader .buffer ))
1892
+
1893
+ def test_ctrl_d_single_line_end_no_newline (self ):
1894
+ """Test that pressing Ctrl+D at end of single line without newline does nothing"""
1895
+ events = itertools .chain (
1896
+ code_to_events ("hello" ), # cursor at end of line
1897
+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1898
+ )
1899
+ reader , _ = handle_all_events (events )
1900
+ self .assertEqual ("hello" , "" .join (reader .buffer ))
0 commit comments