https://github.com/python/cpython/commit/ef1fce0a69a19360643fb82ea3545ede21495c73
commit: ef1fce0a69a19360643fb82ea3545ede21495c73
branch: main
author: Eduardo Villalpando Mello <[email protected]>
committer: brettcannon <[email protected]>
date: 2026-10-29T14:23:07-07:00
summary:

gh-157928: Have PyREPL detect OSC sequences (GH-157932)

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Brett Cannon <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-09-21-21-07-12.gh-issue-157928.ZPlNYC.rst
M Lib/_pyrepl/utils.py
M Lib/test/test_pyrepl/test_render.py
M Lib/test/test_pyrepl/test_utils.py

diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py
index e77001f6a1d10bb..180e111873dbb38 100644
--- a/Lib/_pyrepl/utils.py
+++ b/Lib/_pyrepl/utils.py
@@ -19,7 +19,21 @@
 from .trace import trace
 
 
-ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]")
+ANSI_ESCAPE_SEQUENCE = re.compile(
+    r"""
+    \x1b
+    (?:
+        # CSI: ESC [ followed by parameter/intermediate bytes and a final byte.
+        # For example, ESC [ 31 m ("\x1b[31m") selects red foreground text.
+        \[ [ -@]* [A-~]
+        |
+        # OSC: ESC ] followed by a payload terminated by BEL or ST (ESC \).
+        # For example, ESC ] 0 ; title BEL ("\x1b]0;title\x07") sets the title.
+        \] [^\x1b\x07]* (?: \x07 | \x1b\\ )
+    )
+    """,
+    re.X,
+)
 ZERO_WIDTH_BRACKET = re.compile(r"\x01.*?\x02")
 ZERO_WIDTH_TRANS = str.maketrans({"\x01": "", "\x02": ""})
 IDENTIFIERS_AFTER = frozenset({"def", "class"})
diff --git a/Lib/test/test_pyrepl/test_render.py 
b/Lib/test/test_pyrepl/test_render.py
index 5479f2eb4bae398..7293f0f33f7405f 100644
--- a/Lib/test/test_pyrepl/test_render.py
+++ b/Lib/test/test_pyrepl/test_render.py
@@ -55,6 +55,15 @@ def test_from_rendered_text_empty_string(self):
         self.assertEqual(line.text, "")
         self.assertEqual(line.width, 0)
 
+    def test_from_rendered_text_with_osc_control(self):
+        osc = "\x1b]633;A\x1b\\"
+        prompt = ">>> "
+        line = RenderLine.from_rendered_text(osc + prompt)
+
+        self.assertEqual(line.width, len(prompt))
+        self.assertEqual(line.cells[0].controls, (osc,))
+        self.assertEqual(line.cells[0].text, "")
+
     def test_from_rendered_text_with_non_sgr_controls(self):
         # \x1b[H is a cursor-home control (not SGR since it doesn't end with 
'm')
         line = RenderLine.from_rendered_text("\x1b[Hx")
diff --git a/Lib/test/test_pyrepl/test_utils.py 
b/Lib/test/test_pyrepl/test_utils.py
index e87d14304c82257..5c60770c511c07d 100644
--- a/Lib/test/test_pyrepl/test_utils.py
+++ b/Lib/test/test_pyrepl/test_utils.py
@@ -47,6 +47,14 @@ def test_wlen(self):
         self.assertEqual(wlen('e\N{COMBINING ACUTE ACCENT}'), 1)
         self.assertEqual(wlen('a\N{ZERO WIDTH JOINER}b'), 2)
 
+    def test_wlen_with_osc_sequence(self):
+        prompt = ">>> "
+        self.assertEqual(wlen("\x1b]633;A\x07" + prompt), len(prompt))
+
+    def 
test_wlen_with_unterminated_osc_sequence_as_individual_characters(self):
+        # \x1b is treated as a single character (ESC)
+        self.assertEqual(wlen("\x1b]633;A"), len("\x1b]633;A"))
+
     def test_prev_next_window(self):
         def gen_normal():
             yield 1
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-21-21-07-12.gh-issue-157928.ZPlNYC.rst 
b/Misc/NEWS.d/next/Library/2026-09-21-21-07-12.gh-issue-157928.ZPlNYC.rst
new file mode 100644
index 000000000000000..dfa7a122a9e22e8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-21-21-07-12.gh-issue-157928.ZPlNYC.rst
@@ -0,0 +1 @@
+Correctly detect and handle OSC sequences in PyREPL.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to