https://github.com/python/cpython/commit/dcc4e5247a4885b76b7fc26f5f5809c7a9064e7c
commit: dcc4e5247a4885b76b7fc26f5f5809c7a9064e7c
branch: main
author: Vyron Vasileiadis <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-07T22:21:47+03:00
summary:

gh-156951: Do not truncate the color pair in curses.slk_color() (GH-156952)

slk_color() cast its color pair to a short, so pairs of 32768 and above
were rejected or silently applied as a different pair.  Use
extended_slk_color() when it is available, like slk_attr_set() does.

files:
M Lib/test/test_curses.py
M Modules/_cursesmodule.c

diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index a75b95d988b070..0c63b04a461453 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -3482,10 +3482,10 @@ class SLKTests(NewtermTestBase):
     # slk_init() must run before newterm()/initscr(), so each test sets up its
     # own screen rather than reusing the one TestCurses builds in setUp().
 
-    def make_slk_screen(self, fmt=0):
+    def make_slk_screen(self, fmt=0, term='xterm'):
         s = self.make_pty()
         curses.slk_init(fmt)
-        return curses.newterm('xterm', s, s)
+        return curses.newterm(term, s, s)
 
     def test_init_reserves_a_line(self):
         # Every layout takes the bottom line for the labels; the index-line
@@ -3568,6 +3568,26 @@ def test_color(self):
         curses.slk_attr_set(curses.A_BOLD, 0)
         curses.slk_color(0)
 
+    def test_color_wide_pair(self):
+        # Drive a terminal with enough color pairs to reach past a short,
+        # rather than relying on whatever $TERM happens to be.
+        try:
+            self.make_slk_screen(term='xterm-256color')
+        except curses.error:
+            self.skipTest('no xterm-256color terminfo entry')
+        if not curses.has_colors():
+            self.skipTest('requires colors support')
+        curses.start_color()
+        if not (curses.has_extended_color_support()
+                and curses.COLOR_PAIRS > SHORT_MAX + 1):
+            self.skipTest('requires extended color support')
+        # A pair that does not fit in a short is still a valid pair here.
+        curses.slk_color(SHORT_MAX + 1)
+        # The low 16 bits of this are pair 5, but the pair itself is out of
+        # range, so it must raise instead of selecting pair 5.
+        self.assertRaises(curses.error, curses.slk_color,
+                          curses.COLOR_PAIRS * 2 + 5)
+
 
 @unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
 @unittest.skipIf(BROKEN_NEWTERM, 'ncurses < 6.5 mishandles repeated newterm()')
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 781b39628bfa31..f22aac624a8a66 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -8866,7 +8866,13 @@ _curses_slk_color_impl(PyObject *module, int pair)
 /*[clinic end generated code: output=ffe4de805f9c65f5 input=b1e691a9cc6177ee]*/
 {
     PyCursesStatefulInitialised(module);
-    return curses_check_err(module, slk_color((short)pair), "slk_color", NULL);
+    int rtn;
+#if _NCURSES_EXTENDED_COLOR_FUNCS
+    rtn = extended_slk_color(pair);
+#else
+    rtn = slk_color((short)pair);
+#endif
+    return curses_check_err(module, rtn, "slk_color", NULL);
 }
 #endif /* HAVE_CURSES_SLK_COLOR */
 

_______________________________________________
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