Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
gh-128067: In test_pyrepl, discover escape sequences from terminfo in…
…stead of using hard-coded values
  • Loading branch information
ambv committed Jan 2, 2026
commit b5b2e64d86d2054c84925305d3888649e768df01
61 changes: 43 additions & 18 deletions Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1852,24 +1852,49 @@ def test_no_newline(self):
output, exit_code = self.run_repl(commands)
self.assertEqual(exit_code, 0)

# Define escape sequences that don't affect cursor position or visual output
bracketed_paste_mode = r'\x1b\[\?2004[hl]' # Enable/disable bracketed paste
application_cursor_keys = r'\x1b\[\?1[hl]' # Enable/disable application cursor keys
application_keypad_mode = r'\x1b[=>]' # Enable/disable application keypad
insert_character = r'\x1b\[(?:1)?@(?=[ -~])' # Insert exactly 1 char (safe form)
cursor_visibility = r'\x1b\[\?25[hl]' # Show/hide cursor
cursor_blinking = r'\x1b\[\?12[hl]' # Start/stop cursor blinking
device_attributes = r'\x1b\[\?[01]c' # Device Attributes (DA) queries/responses

safe_escapes = re.compile(
f'{bracketed_paste_mode}|'
f'{application_cursor_keys}|'
f'{application_keypad_mode}|'
f'{insert_character}|'
f'{cursor_visibility}|'
f'{cursor_blinking}|'
f'{device_attributes}'
)
# Build patterns for escape sequences that don't affect cursor position
# or visual output. Use terminfo to get platform-specific sequences,
# falling back to hard-coded patterns for capabilities not in terminfo.
from _pyrepl.terminfo import TermInfo
ti = TermInfo(os.environ.get("TERM", ""))

safe_patterns = []

# smkx/rmkx - application cursor keys and keypad mode
smkx = ti.get("smkx")
rmkx = ti.get("rmkx")
if smkx:
safe_patterns.append(re.escape(smkx.decode("ascii")))
if rmkx:
safe_patterns.append(re.escape(rmkx.decode("ascii")))
if not smkx and not rmkx:
safe_patterns.append(r'\x1b\[\?1[hl]') # application cursor keys
safe_patterns.append(r'\x1b[=>]') # application keypad mode

# ich1 - insert character (only safe form that inserts exactly 1 char)
ich1 = ti.get("ich1")
if ich1:
safe_patterns.append(re.escape(ich1.decode("ascii")) + r'(?=[ -~])')
else:
safe_patterns.append(r'\x1b\[(?:1)?@(?=[ -~])')

# civis/cnorm - cursor visibility (may include cursor blinking control)
civis = ti.get("civis")
cnorm = ti.get("cnorm")
if civis:
safe_patterns.append(re.escape(civis.decode("ascii")))
if cnorm:
safe_patterns.append(re.escape(cnorm.decode("ascii")))
if not civis and not cnorm:
safe_patterns.append(r'\x1b\[\?25[hl]') # cursor visibility
safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking

# Modern extensions not in standard terminfo - always use patterns
safe_patterns.append(r'\x1b\[\?2004[hl]') # bracketed paste mode
safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking (may be separate)
safe_patterns.append(r'\x1b\[\?[01]c') # device attributes

safe_escapes = re.compile('|'.join(safe_patterns))
cleaned_output = safe_escapes.sub('', output)
self.assertIn(expected_output_sequence, cleaned_output)

Expand Down
Loading