]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-153862: Fix spurious color pair in curses window.inch() on a wide build...
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 26 Jul 2026 12:03:32 +0000 (15:03 +0300)
committerGitHub <noreply@github.com>
Sun, 26 Jul 2026 12:03:32 +0000 (15:03 +0300)
winch() returns the whole code point, so inch() replacing only its low
8 bits left the high bits in the color field.  Rebuild from
getcchar()'s attributes and color pair.

(cherry picked from commit b6188749d69e9609a83ee0d526013920da314ab2)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Doc/library/curses.rst
Lib/test/test_curses.py
Modules/_cursesmodule.c

index 768304d2b10b395e3abba2e58f2c7e33777fb99a..8820f5ad4891ed5ad5b31bd506145116cac560ba 100644 (file)
@@ -1065,6 +1065,9 @@ Window objects
    and the color pair with :func:`pair_number`.
    The character byte is the locale-encoded byte of the cell's character,
    consistent with :meth:`instr`.
+   On a wide-character build, a character that does not fit in a single byte
+   in the current locale has a character byte of ``0``;
+   use :meth:`instr` to read such characters.
 
 
 .. method:: window.insch(ch[, attr])
index 5a0697cc3fe9c2a6ef1965ac9badc37fba107eb3..cf9ba5123253529031ef651b9194afc85f12adef 100644 (file)
@@ -523,7 +523,7 @@ class TestCurses(unittest.TestCase):
             with self.subTest(ch=ch):
                 stdscr.addstr(2, 0, ch)
                 self.assertEqual(stdscr.instr(2, 0, 1), b)
-                self.assertEqual(stdscr.inch(2, 0) & curses.A_CHARTEXT, b[0])
+                self.assertEqual(stdscr.inch(2, 0), b[0])
 
     def test_coordinate_errors(self):
         # Addressing a cell outside the window raises curses.error.
index 95dee0469edede6df5e0f6ea9d4d450ad4c675c3..65dcf1bab80833d652721ab2f0d4c90e8f14ad47 100644 (file)
@@ -1736,10 +1736,10 @@ _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1,
 }
 
 #ifdef HAVE_NCURSESW
-/* winch() returns the low 8 bits of the character's code point with no locale
-   conversion, unlike instr(), so recover the locale byte from the wide cell
-   when the character maps to exactly one byte, keeping the attribute and color
-   bits in RTN.  A character with no single-byte form is left to winch(). */
+/* ncursesw's winch() returns the character's whole code point instead of its
+   locale byte, overflowing the chtype's 8-bit character field into the color
+   and attribute bits, so rebuild the value from the locale byte plus the
+   attributes and color pair reported by getcchar(). */
 static chtype
 curses_cell_locale_byte(chtype rtn, const cchar_t *cell)
 {
@@ -1748,18 +1748,19 @@ curses_cell_locale_byte(chtype rtn, const cchar_t *cell)
     short pair;
     /* getcchar() is not guaranteed to write the text of an empty cell. */
     wstr[0] = L'\0';
-    if (getcchar(cell, wstr, &attrs, &pair, NULL) == ERR
-        || wstr[0] == L'\0' || wstr[1] != L'\0')
-    {
+    if (getcchar(cell, wstr, &attrs, &pair, NULL) == ERR) {
         return rtn;
     }
     /* wctob() mirrors ncurses' own _nc_to_char(): the single-byte form, or EOF
-       when the character has none in this locale. */
-    int byte = wctob(wstr[0]);
-    if (byte != EOF) {
-        rtn = (rtn & ~(chtype)A_CHARTEXT) | (unsigned char)byte;
+       when the character has none in this locale (then use 0). */
+    int byte = 0;
+    if (wstr[0] != L'\0' && wstr[1] == L'\0') {
+        byte = wctob(wstr[0]);
+        if (byte == EOF) {
+            byte = 0;
+        }
     }
-    return rtn;
+    return (chtype)byte | (attrs & ~(attr_t)A_COLOR) | COLOR_PAIR(pair);
 }
 #endif