@@ -1836,6 +1836,69 @@ def test_showrefcount(self):
18361836 self .assertEqual (len (matches ), 3 )
18371837
18381838
1839+ @force_not_colorized
1840+ def test_no_newline (self ):
1841+ env = os .environ .copy ()
1842+ env .pop ("PYTHON_BASIC_REPL" , "" )
1843+ env ["PYTHON_BASIC_REPL" ] = "1"
1844+
1845+ commands = "print('Something pretty long', end='')\n exit()\n "
1846+ expected_output_sequence = "Something pretty long>>> exit()"
1847+
1848+ basic_output , basic_exit_code = self .run_repl (commands , env = env )
1849+ self .assertEqual (basic_exit_code , 0 )
1850+ self .assertIn (expected_output_sequence , basic_output )
1851+
1852+ output , exit_code = self .run_repl (commands )
1853+ self .assertEqual (exit_code , 0 )
1854+
1855+ # Build patterns for escape sequences that don't affect cursor position
1856+ # or visual output. Use terminfo to get platform-specific sequences,
1857+ # falling back to hard-coded patterns for capabilities not in terminfo.
1858+ from _pyrepl .terminfo import TermInfo
1859+ ti = TermInfo (os .environ .get ("TERM" , "" ))
1860+
1861+ safe_patterns = []
1862+
1863+ # smkx/rmkx - application cursor keys and keypad mode
1864+ smkx = ti .get ("smkx" )
1865+ rmkx = ti .get ("rmkx" )
1866+ if smkx :
1867+ safe_patterns .append (re .escape (smkx .decode ("ascii" )))
1868+ if rmkx :
1869+ safe_patterns .append (re .escape (rmkx .decode ("ascii" )))
1870+ if not smkx and not rmkx :
1871+ safe_patterns .append (r'\x1b\[\?1[hl]' ) # application cursor keys
1872+ safe_patterns .append (r'\x1b[=>]' ) # application keypad mode
1873+
1874+ # ich1 - insert character (only safe form that inserts exactly 1 char)
1875+ ich1 = ti .get ("ich1" )
1876+ if ich1 :
1877+ safe_patterns .append (re .escape (ich1 .decode ("ascii" )) + r'(?=[ -~])' )
1878+ else :
1879+ safe_patterns .append (r'\x1b\[(?:1)?@(?=[ -~])' )
1880+
1881+ # civis/cnorm - cursor visibility (may include cursor blinking control)
1882+ civis = ti .get ("civis" )
1883+ cnorm = ti .get ("cnorm" )
1884+ if civis :
1885+ safe_patterns .append (re .escape (civis .decode ("ascii" )))
1886+ if cnorm :
1887+ safe_patterns .append (re .escape (cnorm .decode ("ascii" )))
1888+ if not civis and not cnorm :
1889+ safe_patterns .append (r'\x1b\[\?25[hl]' ) # cursor visibility
1890+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking
1891+
1892+ # Modern extensions not in standard terminfo - always use patterns
1893+ safe_patterns .append (r'\x1b\[\?2004[hl]' ) # bracketed paste mode
1894+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking (may be separate)
1895+ safe_patterns .append (r'\x1b\[\?[01]c' ) # device attributes
1896+
1897+ safe_escapes = re .compile ('|' .join (safe_patterns ))
1898+ cleaned_output = safe_escapes .sub ('' , output )
1899+ self .assertIn (expected_output_sequence , cleaned_output )
1900+
1901+
18391902class TestPyReplCtrlD (TestCase ):
18401903 """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior.
18411904
0 commit comments