@@ -1852,24 +1852,49 @@ def test_no_newline(self):
18521852 output , exit_code = self .run_repl (commands )
18531853 self .assertEqual (exit_code , 0 )
18541854
1855- # Define escape sequences that don't affect cursor position or visual output
1856- bracketed_paste_mode = r'\x1b\[\?2004[hl]' # Enable/disable bracketed paste
1857- application_cursor_keys = r'\x1b\[\?1[hl]' # Enable/disable application cursor keys
1858- application_keypad_mode = r'\x1b[=>]' # Enable/disable application keypad
1859- insert_character = r'\x1b\[(?:1)?@(?=[ -~])' # Insert exactly 1 char (safe form)
1860- cursor_visibility = r'\x1b\[\?25[hl]' # Show/hide cursor
1861- cursor_blinking = r'\x1b\[\?12[hl]' # Start/stop cursor blinking
1862- device_attributes = r'\x1b\[\?[01]c' # Device Attributes (DA) queries/responses
1863-
1864- safe_escapes = re .compile (
1865- f'{ bracketed_paste_mode } |'
1866- f'{ application_cursor_keys } |'
1867- f'{ application_keypad_mode } |'
1868- f'{ insert_character } |'
1869- f'{ cursor_visibility } |'
1870- f'{ cursor_blinking } |'
1871- f'{ device_attributes } '
1872- )
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 ))
18731898 cleaned_output = safe_escapes .sub ('' , output )
18741899 self .assertIn (expected_output_sequence , cleaned_output )
18751900
0 commit comments