@@ -1404,3 +1404,87 @@ def test_showrefcount(self):
14041404 output , _ = self .run_repl ("1\n 1+2\n exit()\n " , cmdline_args = ['-Xshowrefcount' ], env = env )
14051405 matches = re .findall (r'\[-?\d+ refs, \d+ blocks\]' , output )
14061406 self .assertEqual (len (matches ), 3 )
1407+
1408+
1409+ class TestPyReplCtrlD (TestCase ):
1410+ """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior.
1411+
1412+ Ctrl+D should:
1413+ - Exit on empty buffer (raises EOFError)
1414+ - Delete character when cursor is in middle of line
1415+ - Perform no operation when cursor is at end of line without newline
1416+ - Exit multiline mode when cursor is at end with trailing newline
1417+ - Run code up to that point when pressed on blank line with preceding lines
1418+ """
1419+ def prepare_reader (self , events ):
1420+ console = FakeConsole (events )
1421+ config = ReadlineConfig (readline_completer = None )
1422+ reader = ReadlineAlikeReader (console = console , config = config )
1423+ return reader
1424+
1425+ def test_ctrl_d_empty_line (self ):
1426+ """Test that pressing Ctrl+D on empty line exits the program"""
1427+ events = [
1428+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1429+ ]
1430+ reader = self .prepare_reader (events )
1431+ with self .assertRaises (EOFError ):
1432+ multiline_input (reader )
1433+
1434+ def test_ctrl_d_multiline_with_new_line (self ):
1435+ """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode"""
1436+ events = itertools .chain (
1437+ code_to_events ("def f():\n pass\n " ), # Enter multiline mode with trailing newline
1438+ [
1439+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1440+ ],
1441+ )
1442+ reader , _ = handle_all_events (events )
1443+ self .assertTrue (reader .finished )
1444+ self .assertEqual ("def f():\n pass\n " , "" .join (reader .buffer ))
1445+
1446+ def test_ctrl_d_multiline_middle_of_line (self ):
1447+ """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character"""
1448+ events = itertools .chain (
1449+ code_to_events ("def f():\n hello world" ), # Enter multiline mode
1450+ [
1451+ Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))
1452+ ] * 5 , # move cursor to 'w' in "world"
1453+ [
1454+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1455+ ], # Ctrl+D should delete 'w'
1456+ )
1457+ reader , _ = handle_all_events (events )
1458+ self .assertFalse (reader .finished )
1459+ self .assertEqual ("def f():\n hello orld" , "" .join (reader .buffer ))
1460+
1461+ def test_ctrl_d_multiline_end_of_line_no_newline (self ):
1462+ """Test that pressing Ctrl+D at end of line without newline performs no operation"""
1463+ events = itertools .chain (
1464+ code_to_events ("def f():\n hello" ), # Enter multiline mode, no trailing newline
1465+ [
1466+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1467+ ], # Ctrl+D should be no-op
1468+ )
1469+ reader , _ = handle_all_events (events )
1470+ self .assertFalse (reader .finished )
1471+ self .assertEqual ("def f():\n hello" , "" .join (reader .buffer ))
1472+
1473+ def test_ctrl_d_single_line_middle_of_line (self ):
1474+ """Test that pressing Ctrl+D in single line mode deletes current character"""
1475+ events = itertools .chain (
1476+ code_to_events ("hello" ),
1477+ [Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))], # move left
1478+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1479+ )
1480+ reader , _ = handle_all_events (events )
1481+ self .assertEqual ("hell" , "" .join (reader .buffer ))
1482+
1483+ def test_ctrl_d_single_line_end_no_newline (self ):
1484+ """Test that pressing Ctrl+D at end of single line without newline does nothing"""
1485+ events = itertools .chain (
1486+ code_to_events ("hello" ), # cursor at end of line
1487+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1488+ )
1489+ reader , _ = handle_all_events (events )
1490+ self .assertEqual ("hello" , "" .join (reader .buffer ))
0 commit comments