@@ -1862,24 +1862,49 @@ def test_no_newline(self):
18621862 output , exit_code = self .run_repl (commands )
18631863 self .assertEqual (exit_code , 0 )
18641864
1865- # Define escape sequences that don't affect cursor position or visual output
1866- bracketed_paste_mode = r'\x1b\[\?2004[hl]' # Enable/disable bracketed paste
1867- application_cursor_keys = r'\x1b\[\?1[hl]' # Enable/disable application cursor keys
1868- application_keypad_mode = r'\x1b[=>]' # Enable/disable application keypad
1869- insert_character = r'\x1b\[(?:1)?@(?=[ -~])' # Insert exactly 1 char (safe form)
1870- cursor_visibility = r'\x1b\[\?25[hl]' # Show/hide cursor
1871- cursor_blinking = r'\x1b\[\?12[hl]' # Start/stop cursor blinking
1872- device_attributes = r'\x1b\[\?[01]c' # Device Attributes (DA) queries/responses
1873-
1874- safe_escapes = re .compile (
1875- f'{ bracketed_paste_mode } |'
1876- f'{ application_cursor_keys } |'
1877- f'{ application_keypad_mode } |'
1878- f'{ insert_character } |'
1879- f'{ cursor_visibility } |'
1880- f'{ cursor_blinking } |'
1881- f'{ device_attributes } '
1882- )
1865+ # Build patterns for escape sequences that don't affect cursor position
1866+ # or visual output. Use terminfo to get platform-specific sequences,
1867+ # falling back to hard-coded patterns for capabilities not in terminfo.
1868+ from _pyrepl .terminfo import TermInfo
1869+ ti = TermInfo (os .environ .get ("TERM" , "" ))
1870+
1871+ safe_patterns = []
1872+
1873+ # smkx/rmkx - application cursor keys and keypad mode
1874+ smkx = ti .get ("smkx" )
1875+ rmkx = ti .get ("rmkx" )
1876+ if smkx :
1877+ safe_patterns .append (re .escape (smkx .decode ("ascii" )))
1878+ if rmkx :
1879+ safe_patterns .append (re .escape (rmkx .decode ("ascii" )))
1880+ if not smkx and not rmkx :
1881+ safe_patterns .append (r'\x1b\[\?1[hl]' ) # application cursor keys
1882+ safe_patterns .append (r'\x1b[=>]' ) # application keypad mode
1883+
1884+ # ich1 - insert character (only safe form that inserts exactly 1 char)
1885+ ich1 = ti .get ("ich1" )
1886+ if ich1 :
1887+ safe_patterns .append (re .escape (ich1 .decode ("ascii" )) + r'(?=[ -~])' )
1888+ else :
1889+ safe_patterns .append (r'\x1b\[(?:1)?@(?=[ -~])' )
1890+
1891+ # civis/cnorm - cursor visibility (may include cursor blinking control)
1892+ civis = ti .get ("civis" )
1893+ cnorm = ti .get ("cnorm" )
1894+ if civis :
1895+ safe_patterns .append (re .escape (civis .decode ("ascii" )))
1896+ if cnorm :
1897+ safe_patterns .append (re .escape (cnorm .decode ("ascii" )))
1898+ if not civis and not cnorm :
1899+ safe_patterns .append (r'\x1b\[\?25[hl]' ) # cursor visibility
1900+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking
1901+
1902+ # Modern extensions not in standard terminfo - always use patterns
1903+ safe_patterns .append (r'\x1b\[\?2004[hl]' ) # bracketed paste mode
1904+ safe_patterns .append (r'\x1b\[\?12[hl]' ) # cursor blinking (may be separate)
1905+ safe_patterns .append (r'\x1b\[\?[01]c' ) # device attributes
1906+
1907+ safe_escapes = re .compile ('|' .join (safe_patterns ))
18831908 cleaned_output = safe_escapes .sub ('' , output )
18841909 self .assertIn (expected_output_sequence , cleaned_output )
18851910
0 commit comments