Skip to content

Commit 4c41f8b

Browse files
committed
gh-128067: In test_pyrepl, discover escape sequences from terminfo instead of using hard-coded values
1 parent 8a2deea commit 4c41f8b

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
@@ -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

Comments
 (0)