@@ -1814,3 +1814,87 @@ def test_showrefcount(self):
18141814 output , _ = self .run_repl ("1\n 1+2\n exit()\n " , cmdline_args = ['-Xshowrefcount' ], env = env )
18151815 matches = re .findall (r'\[-?\d+ refs, \d+ blocks\]' , output )
18161816 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