]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128067: In test_pyrepl, discover escape sequences from terminfo instead of using...
authorŁukasz Langa <lukasz@langa.pl>
Fri, 2 Jan 2026 14:42:04 +0000 (15:42 +0100)
committerGitHub <noreply@github.com>
Fri, 2 Jan 2026 14:42:04 +0000 (15:42 +0100)
Lib/test/test_pyrepl/test_pyrepl.py

index 90cadd0b2ad494308d66bf0412ec15e06376422b..6cf87522af2bc30c0f836096cc76d2ae8298dab1 100644 (file)
@@ -1862,24 +1862,49 @@ class TestMain(ReplTestCase):
         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)