Skip to content

Commit 7ac58e5

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

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
@@ -1425,24 +1425,49 @@ def test_no_newline(self):
14251425
output, exit_code = self.run_repl(commands)
14261426
self.assertEqual(exit_code, 0)
14271427

1428-
# Define escape sequences that don't affect cursor position or visual output
1429-
bracketed_paste_mode = r'\x1b\[\?2004[hl]' # Enable/disable bracketed paste
1430-
application_cursor_keys = r'\x1b\[\?1[hl]' # Enable/disable application cursor keys
1431-
application_keypad_mode = r'\x1b[=>]' # Enable/disable application keypad
1432-
insert_character = r'\x1b\[(?:1)?@(?=[ -~])' # Insert exactly 1 char (safe form)
1433-
cursor_visibility = r'\x1b\[\?25[hl]' # Show/hide cursor
1434-
cursor_blinking = r'\x1b\[\?12[hl]' # Start/stop cursor blinking
1435-
device_attributes = r'\x1b\[\?[01]c' # Device Attributes (DA) queries/responses
1436-
1437-
safe_escapes = re.compile(
1438-
f'{bracketed_paste_mode}|'
1439-
f'{application_cursor_keys}|'
1440-
f'{application_keypad_mode}|'
1441-
f'{insert_character}|'
1442-
f'{cursor_visibility}|'
1443-
f'{cursor_blinking}|'
1444-
f'{device_attributes}'
1445-
)
1428+
# Build patterns for escape sequences that don't affect cursor position
1429+
# or visual output. Use terminfo to get platform-specific sequences,
1430+
# falling back to hard-coded patterns for capabilities not in terminfo.
1431+
from _pyrepl.terminfo import TermInfo
1432+
ti = TermInfo(os.environ.get("TERM", ""))
1433+
1434+
safe_patterns = []
1435+
1436+
# smkx/rmkx - application cursor keys and keypad mode
1437+
smkx = ti.get("smkx")
1438+
rmkx = ti.get("rmkx")
1439+
if smkx:
1440+
safe_patterns.append(re.escape(smkx.decode("ascii")))
1441+
if rmkx:
1442+
safe_patterns.append(re.escape(rmkx.decode("ascii")))
1443+
if not smkx and not rmkx:
1444+
safe_patterns.append(r'\x1b\[\?1[hl]') # application cursor keys
1445+
safe_patterns.append(r'\x1b[=>]') # application keypad mode
1446+
1447+
# ich1 - insert character (only safe form that inserts exactly 1 char)
1448+
ich1 = ti.get("ich1")
1449+
if ich1:
1450+
safe_patterns.append(re.escape(ich1.decode("ascii")) + r'(?=[ -~])')
1451+
else:
1452+
safe_patterns.append(r'\x1b\[(?:1)?@(?=[ -~])')
1453+
1454+
# civis/cnorm - cursor visibility (may include cursor blinking control)
1455+
civis = ti.get("civis")
1456+
cnorm = ti.get("cnorm")
1457+
if civis:
1458+
safe_patterns.append(re.escape(civis.decode("ascii")))
1459+
if cnorm:
1460+
safe_patterns.append(re.escape(cnorm.decode("ascii")))
1461+
if not civis and not cnorm:
1462+
safe_patterns.append(r'\x1b\[\?25[hl]') # cursor visibility
1463+
safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking
1464+
1465+
# Modern extensions not in standard terminfo - always use patterns
1466+
safe_patterns.append(r'\x1b\[\?2004[hl]') # bracketed paste mode
1467+
safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking (may be separate)
1468+
safe_patterns.append(r'\x1b\[\?[01]c') # device attributes
1469+
1470+
safe_escapes = re.compile('|'.join(safe_patterns))
14461471
cleaned_output = safe_escapes.sub('', output)
14471472
self.assertIn(expected_output_sequence, cleaned_output)
14481473

0 commit comments

Comments
 (0)