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