https://github.com/python/cpython/commit/0923704b49e4b390eda4085e97cae590405660d4
commit: 0923704b49e4b390eda4085e97cae590405660d4
branch: 3.13
author: Stan Ulbrych <[email protected]>
committer: encukou <[email protected]>
date: 2025-11-07T13:51:03+01:00
summary:

[3.13] gh-139246: zero-width word paste can be wrong in default repl 
(GH-139254) (GH-141166)

(cherry picked from commit 4e6dba0ef74523a52f66547c16b9972664b18fd4)

Signed-off-by: yihong0618 <[email protected]>
Co-authored-by: yihong <[email protected]>
Co-authored-by: grayjk <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-09-23-09-46-46.gh-issue-139246.pzfM-w.rst
M Lib/_pyrepl/utils.py
M Lib/test/test_pyrepl/test_utils.py

diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py
index 7437fbe1ab9371..a30fbdee3a414f 100644
--- a/Lib/_pyrepl/utils.py
+++ b/Lib/_pyrepl/utils.py
@@ -14,6 +14,12 @@
 def str_width(c: str) -> int:
     if ord(c) < 128:
         return 1
+    # gh-139246 for zero-width joiner and combining characters
+    if unicodedata.combining(c):
+        return 0
+    category = unicodedata.category(c)
+    if category == "Cf" and c != "\u00ad":
+        return 0
     w = unicodedata.east_asian_width(c)
     if w in ("N", "Na", "H", "A"):
         return 1
diff --git a/Lib/test/test_pyrepl/test_utils.py 
b/Lib/test/test_pyrepl/test_utils.py
index 0d59968206a613..70e2484ab5f3a1 100644
--- a/Lib/test/test_pyrepl/test_utils.py
+++ b/Lib/test/test_pyrepl/test_utils.py
@@ -5,10 +5,29 @@
 
 class TestUtils(TestCase):
     def test_str_width(self):
-        characters = ['a', '1', '_', '!', '\x1a', '\u263A', '\uffb9']
+        characters = [
+            'a',
+            '1',
+            '_',
+            '!',
+            '\x1a',
+            '\u263A',
+            '\uffb9',
+            '\N{LATIN SMALL LETTER E WITH ACUTE}',  # é
+            '\N{LATIN SMALL LETTER E WITH CEDILLA}', # ȩ
+            '\u00ad',
+        ]
         for c in characters:
             self.assertEqual(str_width(c), 1)
 
+        zero_width_characters = [
+            '\N{COMBINING ACUTE ACCENT}',
+            '\N{ZERO WIDTH JOINER}',
+        ]
+        for c in zero_width_characters:
+            with self.subTest(character=c):
+                self.assertEqual(str_width(c), 0)
+
         characters = [chr(99989), chr(99999)]
         for c in characters:
             self.assertEqual(str_width(c), 2)
@@ -25,3 +44,5 @@ def test_wlen(self):
 
         self.assertEqual(wlen('hello'), 5)
         self.assertEqual(wlen('hello' + '\x1a'), 7)
+        self.assertEqual(wlen('e\N{COMBINING ACUTE ACCENT}'), 1)
+        self.assertEqual(wlen('a\N{ZERO WIDTH JOINER}b'), 2)
diff --git 
a/Misc/NEWS.d/next/Library/2025-09-23-09-46-46.gh-issue-139246.pzfM-w.rst 
b/Misc/NEWS.d/next/Library/2025-09-23-09-46-46.gh-issue-139246.pzfM-w.rst
new file mode 100644
index 00000000000000..a816bda5cfe8e8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-09-23-09-46-46.gh-issue-139246.pzfM-w.rst
@@ -0,0 +1 @@
+fix: paste zero-width in default repl width is wrong.

_______________________________________________
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