Skip to content

Commit b5b2e64

Browse files
committed
gh-128067: In test_pyrepl, discover escape sequences from terminfo instead of using hard-coded values
1 parent 2a7dcf0 commit b5b2e64

File tree

1 file changed

+43
-18
lines changed

1 file changed

+43
-18
lines changed

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)