From dc4cc3dd16387cd7ff1b9b2b6ebac561221e0691 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 25 Aug 2026 11:38:56 +0300 Subject: [PATCH] gh-156348: Fix the value of curses.ERR Setting the module constants with an unsigned conversion, needed for the chtype constants that can set bits beyond a 32-bit long, turned ERR from -1 into 18446744073709551615. Set ERR and OK with a signed conversion. --- Lib/test/test_curses.py | 5 +++++ Modules/_cursesmodule.c | 29 ++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 630de544a457f40..dde236a4578f2eb 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -2967,6 +2967,11 @@ def test_has_extended_color_support(self): r = curses.has_extended_color_support() self.assertIsInstance(r, bool) + def test_err_and_ok(self): + # ERR is negative; it is not a chtype constant. + self.assertEqual(curses.ERR, -1) + self.assertEqual(curses.OK, 0) + def test_type_names(self): # The curses types report their public module rather than the # underscore extension that implements them. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index bd13022261c20ea..2ff15dd31d21803 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -6657,11 +6657,10 @@ curses_init_dict(PyObject *module) } /* This was moved from initcurses() because it core dumped on SGI, where they're not defined until you've called initscr() */ - /* Use long long, not long: a chtype constant (the A_* attributes, ACS_* - and key codes) can set bits beyond a 32-bit long, which is what long is - on LLP64 platforms such as Windows -- A_DIM (0x80000000) would otherwise - be sign-extended to a negative number. long long is at least 64 bits - everywhere and still represents the negative ERR (-1). */ + /* Use unsigned long long, not long: a chtype constant (the A_* attributes, + ACS_* and key codes) can set bits beyond a 32-bit long, which is what + long is on LLP64 platforms such as Windows -- A_DIM (0x80000000) would + otherwise be sign-extended to a negative number. */ #define SetDictInt(NAME, VALUE) \ do { \ PyObject *value = PyLong_FromUnsignedLongLong((unsigned long long)(VALUE)); \ @@ -9419,8 +9418,24 @@ cursesmodule_exec(PyObject *module) } \ } while (0) - SetDictInt("ERR", ERR); - SetDictInt("OK", OK); + /* ERR is -1, so it needs a signed conversion, unlike the chtype + constants below. */ +#define SetDictSignedInt(NAME, VALUE) \ + do { \ + PyObject *value = PyLong_FromLongLong((long long)(VALUE)); \ + if (value == NULL) { \ + return -1; \ + } \ + int rc = PyDict_SetItemString(module_dict, (NAME), value); \ + Py_DECREF(value); \ + if (rc < 0) { \ + return -1; \ + } \ + } while (0) + + SetDictSignedInt("ERR", ERR); + SetDictSignedInt("OK", OK); +#undef SetDictSignedInt /* Here are some attributes you can add to chars to print */