@@ -1814,3 +1814,98 @@ 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+ def test_detect_pip_usage_in_repl (self ):
1819+ for pip_cmd in ("pip" , "pip3" , "python -m pip" , "python3 -m pip" ):
1820+ with self .subTest (pip_cmd = pip_cmd ):
1821+ output , exit_code = self .run_repl ([f"{ pip_cmd } install sampleproject" , "exit" ])
1822+ self .assertIn ("SyntaxError" , output )
1823+ hint = (
1824+ "The Python package manager (pip) can only be used"
1825+ " outside of the Python REPL"
1826+ )
1827+ self .assertIn (hint , output )
1828+
1829+
1830+ class TestPyReplCtrlD (TestCase ):
1831+ """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior.
1832+
1833+ Ctrl+D should:
1834+ - Exit on empty buffer (raises EOFError)
1835+ - Delete character when cursor is in middle of line
1836+ - Perform no operation when cursor is at end of line without newline
1837+ - Exit multiline mode when cursor is at end with trailing newline
1838+ - Run code up to that point when pressed on blank line with preceding lines
1839+ """
1840+ def prepare_reader (self , events ):
1841+ console = FakeConsole (events )
1842+ config = ReadlineConfig (readline_completer = None )
1843+ reader = ReadlineAlikeReader (console = console , config = config )
1844+ return reader
1845+
1846+ def test_ctrl_d_empty_line (self ):
1847+ """Test that pressing Ctrl+D on empty line exits the program"""
1848+ events = [
1849+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1850+ ]
1851+ reader = self .prepare_reader (events )
1852+ with self .assertRaises (EOFError ):
1853+ multiline_input (reader )
1854+
1855+ def test_ctrl_d_multiline_with_new_line (self ):
1856+ """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode"""
1857+ events = itertools .chain (
1858+ code_to_events ("def f():\n pass\n " ), # Enter multiline mode with trailing newline
1859+ [
1860+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " )), # Ctrl+D
1861+ ],
1862+ )
1863+ reader , _ = handle_all_events (events )
1864+ self .assertTrue (reader .finished )
1865+ self .assertEqual ("def f():\n pass\n " , "" .join (reader .buffer ))
1866+
1867+ def test_ctrl_d_multiline_middle_of_line (self ):
1868+ """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character"""
1869+ events = itertools .chain (
1870+ code_to_events ("def f():\n hello world" ), # Enter multiline mode
1871+ [
1872+ Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))
1873+ ] * 5 , # move cursor to 'w' in "world"
1874+ [
1875+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1876+ ], # Ctrl+D should delete 'w'
1877+ )
1878+ reader , _ = handle_all_events (events )
1879+ self .assertFalse (reader .finished )
1880+ self .assertEqual ("def f():\n hello orld" , "" .join (reader .buffer ))
1881+
1882+ def test_ctrl_d_multiline_end_of_line_no_newline (self ):
1883+ """Test that pressing Ctrl+D at end of line without newline performs no operation"""
1884+ events = itertools .chain (
1885+ code_to_events ("def f():\n hello" ), # Enter multiline mode, no trailing newline
1886+ [
1887+ Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))
1888+ ], # Ctrl+D should be no-op
1889+ )
1890+ reader , _ = handle_all_events (events )
1891+ self .assertFalse (reader .finished )
1892+ self .assertEqual ("def f():\n hello" , "" .join (reader .buffer ))
1893+
1894+ def test_ctrl_d_single_line_middle_of_line (self ):
1895+ """Test that pressing Ctrl+D in single line mode deletes current character"""
1896+ events = itertools .chain (
1897+ code_to_events ("hello" ),
1898+ [Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))], # move left
1899+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1900+ )
1901+ reader , _ = handle_all_events (events )
1902+ self .assertEqual ("hell" , "" .join (reader .buffer ))
1903+
1904+ def test_ctrl_d_single_line_end_no_newline (self ):
1905+ """Test that pressing Ctrl+D at end of single line without newline does nothing"""
1906+ events = itertools .chain (
1907+ code_to_events ("hello" ), # cursor at end of line
1908+ [Event (evt = "key" , data = "\x04 " , raw = bytearray (b"\x04 " ))], # Ctrl+D
1909+ )
1910+ reader , _ = handle_all_events (events )
1911+ self .assertEqual ("hello" , "" .join (reader .buffer ))
0 commit comments