https://github.com/python/cpython/commit/fe8ace30aeeb5991c59e2dcb65d8c32f0c262d26
commit: fe8ace30aeeb5991c59e2dcb65d8c32f0c262d26
branch: main
author: Vyron Vasileiadis <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-25T08:30:58Z
summary:
gh-156207: Fix curses Textbox.gather() for double-width characters (GH-156208)
gather() read the window cell by cell, so a double-width character, which
occupies two cells, was returned twice. Read the whole line with in_wstr(),
which returns every character once.
files:
M Lib/curses/textpad.py
M Lib/test/test_curses.py
diff --git a/Lib/curses/textpad.py b/Lib/curses/textpad.py
index f6dfc990901d995..31ad37539ca4a42 100644
--- a/Lib/curses/textpad.py
+++ b/Lib/curses/textpad.py
@@ -200,14 +200,16 @@ def gather(self):
result = ""
self._update_max_yx()
for y in range(self.maxy+1):
- self.win.move(y, 0)
- stop = self._end_of_line(y)
- if stop == 0 and self.stripspaces:
- continue
- for x in range(self.maxx+1):
- if self.stripspaces and x > stop:
- break
- result = result + str(self.win.in_wch(y, x))
+ # The whole line: in_wstr() reads a double-width character once,
+ # skipping the continuation cell that holds its other half.
+ line = self.win.in_wstr(y, 0)
+ if self.stripspaces:
+ stripped = line.rstrip(' ')
+ if not stripped:
+ continue
+ # Keep the blank the cursor rests on past the last character.
+ line = stripped + ' ' if len(stripped) < len(line) else
stripped
+ result = result + line
if self.maxy > 0:
result = result + "\n"
return result
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index 630de544a457f40..13b024941f739b3 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -2694,6 +2694,23 @@ def test_textbox_combining(self):
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')
+ @requires_wide_build
+ def test_textbox_double_width(self):
+ # A double-width (East Asian) character occupies two cells. gather()
+ # reads a whole line at a time so that the second cell, which holds
+ # the same character, is not reported as another one.
+ text = '你好'
+ if not self._encodable(text):
+ self.skipTest('the locale cannot encode %r' % text)
+ box, win = self._make_textbox(1, 12)
+ for ch in text:
+ box.do_command(ch)
+ self.assertEqual(box.gather(), text + ' ')
+ box, win = self._make_textbox(1, 12, stripspaces=False)
+ for ch in text:
+ box.do_command(ch)
+ self.assertEqual(box.gather(), text + ' ' * 8)
+
def test_textbox_edit_wide(self):
# edit() reads characters through get_wch(). Each character is pushed
# with unget_wch(), which on a narrow build requires it to encode to a
_______________________________________________
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]