]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-154934: Remove ncurses assumptions from curses and its tests (GH-154940)
authorSerhiy Storchaka <storchaka@gmail.com>
Thu, 30 Jul 2026 18:12:29 +0000 (21:12 +0300)
committerGitHub <noreply@github.com>
Thu, 30 Jul 2026 18:12:29 +0000 (21:12 +0300)
Parse the integer forms of addch() and friends as unsigned long long: the
width of a chtype is up to the library.

Expose _curses._wide_character_support so that the tests can ask what a
cell holds instead of probing for a function.

Mask an integer keystroke in textpad as the byte it is, and keep the key
codes out of the printable range.

Lib/curses/textpad.py
Lib/test/test_curses.py
Modules/_cursesmodule.c
Modules/clinic/_cursesmodule.c.h

index 70fa2c25f64632852a6f058fa8647d76f65a4010..f6dfc990901d995f1460e7ab385e99dd1349d9ec 100644 (file)
@@ -57,14 +57,17 @@ class Textbox:
         self.maxx = maxx - 1
 
     def _decode(self, ch):
-        # Decode an integer keystroke or byte to text with the window's encoding.
-        # A_CHARTEXT drops any attribute bits.
-        return bytes([ch & curses.A_CHARTEXT]).decode(self.win.encoding, 'replace')
+        # An integer keystroke is a byte: get_wch() passes a character as a
+        # string, and getch() reports one as its bytes in the encoding.
+        return bytes([ch & 0xff]).decode(self.win.encoding, 'replace')
 
     def _printable_key(self, ch):
-        # Whether the integer keystroke is a printable character, not a key code:
-        # 0..255 are character bytes, larger values are function keys.
-        return ch <= 0xff and self._decode(ch).isprintable()
+        # Whether the integer keystroke is a printable character rather than a
+        # key code.  Key codes occupy [curses.KEY_MIN, curses.KEY_MAX], which
+        # may start above a byte.
+        return (ch <= 0xff
+                and not curses.KEY_MIN <= ch <= curses.KEY_MAX
+                and self._decode(ch).isprintable())
 
     def _end_of_line(self, y):
         """Go to the location of the first blank on the given line,
@@ -93,14 +96,18 @@ class Textbox:
             if y >= self.maxy and x >= self.maxx:
                 # Use insch() in the lower-right cell; addch() there would push
                 # the cursor out of the window (an error, and it scrolls a
-                # scrollable window).  insch() does not decode an int byte
-                # through the locale on a wide build, so pass it as text.
+                # scrollable window).
                 if isinstance(ch, int):
                     self.win.insch(self._decode(ch), ch & curses.A_ATTRIBUTES)
                 else:
                     self.win.insch(ch)
                 break
-            self.win.addch(ch)
+            # Pass the character as text: an integer is a byte, which addch()
+            # takes as a code point on a wide build.
+            if isinstance(ch, int):
+                self.win.addch(self._decode(ch), ch & curses.A_ATTRIBUTES)
+            else:
+                self.win.addch(ch)
             # In insert mode keep shifting cells right until a blank one.
             if not self.insert_mode or not str(oldch).isprintable():
                 break
index 4ba210c162d0f6d8811614be50f94a482de76244..845b9a135a2424381fb1829d10e9de7bf2817d1a 100644 (file)
@@ -46,22 +46,7 @@ def requires_curses_window_meth(name):
         return wrapped
     return deco
 
-def _wide_build():
-    # True on a build that stores wide-character cells (built against ncursesw).
-    # A wide build accepts a spacing character plus a combining mark in a single
-    # cell; a narrow build accepts only one character per cell.  This stays a
-    # reliable wide/narrow signal even as the wide-character functions (get_wch()
-    # and friends) become available on narrow builds too, because the
-    # multi-codepoint cell capacity itself is build-specific.
-    if not hasattr(curses, 'complexchar'):
-        return hasattr(curses.window, 'get_wch')
-    try:
-        curses.complexchar('e\u0301')  # 'e' + combining acute: two code points
-    except ValueError:
-        return False
-    return True
-
-WIDE_BUILD = _wide_build()
+WIDE_BUILD = import_module('_curses')._wide_character_support
 
 def requires_wide_build(test):
     @functools.wraps(test)
@@ -404,6 +389,15 @@ class TestCurses(unittest.TestCase):
             return True
         return len(s.encode(self.stdscr.encoding)) == len(s)
 
+    def _char_code(self, ch):
+        # The integer the int-input API (addch(int), do_command()) uses for a
+        # character, or None if it has none: a cell holds a single locale byte.
+        try:
+            b = ch.encode(self.stdscr.encoding)
+        except UnicodeEncodeError:
+            return None
+        return b[0] if len(b) == 1 else None
+
     def _read_char(self, y, x):
         # The character written to a cell, read back for output checks.  inch()
         # is unusable here: on a wide build it returns the low 8 bits of the
@@ -783,18 +777,15 @@ class TestCurses(unittest.TestCase):
         stdscr.addch(2, 3, 'A', curses.A_BOLD)
         self.assertIs(stdscr.is_wintouched(), True)
 
-        # The same characters supplied as an int chtype (a byte > 127).  The
-        # cell is read back with _read_char(), not inch(): on a wide build the
-        # int is stored through the locale as a wide character that inch()
-        # cannot represent for a character outside Latin-1.
+        # The same characters supplied as an int chtype.  The cell is read back
+        # with _read_char(), not inch(): on a wide build the int is stored as a
+        # wide character that inch() cannot represent for a character outside
+        # Latin-1.  The int is decoded as a locale byte, so only a single-byte
+        # character round-trips.
         for c in ('é', '¤', '€', 'є'):
-            try:
-                b = c.encode(encoding)
-            except UnicodeEncodeError:
-                continue
-            if len(b) != 1:
+            v = self._char_code(c)
+            if v is None:
                 continue
-            v = b[0]
             with self.subTest(c=c):
                 stdscr.addch(0, 0, v)
                 self.assertEqual(self._read_char(0, 0), c)
@@ -973,22 +964,21 @@ class TestCurses(unittest.TestCase):
         self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ')
         self.assertRaises(ValueError, stdscr.instr, -2)
         self.assertRaises(ValueError, stdscr.instr, 0, 2, -2)
-        # A non-ASCII character of an 8-bit locale reads back as its encoded
-        # byte (see _encodable for the set).  Both instr() and inch() return the
-        # locale byte for any character that fits the locale's single-byte
-        # encoding.
-        encoding = stdscr.encoding
+        # instr(y, x, 1) reads a single cell byte, so only a character that the
+        # window encoding maps to one byte is checked.  inch() returns the cell
+        # value, which is the locale byte.
         for ch in ('A', 'é', '¤', '€', 'є'):
             try:
-                b = ch.encode(encoding)
+                b = ch.encode(stdscr.encoding)
             except UnicodeEncodeError:
                 continue
             if len(b) != 1:
                 continue
+            v = self._char_code(ch)
             with self.subTest(ch=ch):
                 stdscr.addstr(2, 0, ch)
                 self.assertEqual(stdscr.instr(2, 0, 1), b)
-                self.assertEqual(stdscr.inch(2, 0), b[0])
+                self.assertEqual(stdscr.inch(2, 0), v)
 
     def test_coordinate_errors(self):
         # Addressing a cell outside the window raises curses.error.
@@ -1030,9 +1020,23 @@ class TestCurses(unittest.TestCase):
         self.assertEqual(win.getch(), b'm'[0])
         self.assertEqual(win.getch(), b'\n'[0])
 
-        # A key value > 127 is delivered unchanged (it is not locale text).
-        curses.ungetch(0xE9)
-        self.assertEqual(win.getch(), 0xE9)
+        # A non-ASCII character encodable as a single byte in the locale
+        # round-trips as that byte.
+        encoding = self.stdscr.encoding
+        for ch in ('é', '¤', '€', 'є'):
+            try:
+                b = ch.encode(encoding)
+            except UnicodeEncodeError:
+                continue
+            if len(b) != 1:
+                continue
+            with self.subTest(ch=ch):
+                curses.ungetch(self._char_code(ch))
+                self.assertEqual(win.getch(), b[0])
+
+        # A key code is delivered unchanged.
+        curses.ungetch(curses.KEY_LEFT)
+        self.assertEqual(win.getch(), curses.KEY_LEFT)
 
     def test_getstr(self):
         win = curses.newwin(5, 12, 5, 2)
@@ -1277,28 +1281,24 @@ class TestCurses(unittest.TestCase):
         self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE)
         self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE)
 
-        # A non-ASCII background character of an 8-bit locale reads back as its
-        # encoded byte.  See _encodable for the character set.
+        # A non-ASCII background character reads back as its cell value, the
+        # locale byte.
         win.bkgd(' ')
-        encoding = win.encoding
         for ch in ('é', '¤', '€', 'є'):
-            try:
-                b = ch.encode(encoding)
-            except UnicodeEncodeError:
-                continue
-            if len(b) != 1:
+            v = self._char_code(ch)
+            if v is None:
                 continue
             with self.subTest(ch=ch):
                 win.bkgd(ch)
-                self.assertEqual(win.getbkgd(), b[0])
+                self.assertEqual(win.getbkgd(), v)
                 if ord(ch) < 0x100:
                     # The same byte given as an int.  A wide build stores it
                     # through the locale, so only a Latin-1 byte round-trips.
                     win.bkgd(' ')
-                    win.bkgdset(b[0])
-                    self.assertEqual(win.getbkgd(), b[0])
-                    win.bkgd(b[0])
-                    self.assertEqual(win.getbkgd(), b[0])
+                    win.bkgdset(v)
+                    self.assertEqual(win.getbkgd(), v)
+                    win.bkgd(v)
+                    self.assertEqual(win.getbkgd(), v)
 
     def test_overlay(self):
         srcwin = curses.newwin(5, 18, 3, 4)
@@ -1488,20 +1488,21 @@ class TestCurses(unittest.TestCase):
         self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT)
         self.assertEqual(win.inch(3, 1), b'a'[0])
 
-        # A border or line character of an 8-bit locale round-trips as its
-        # encoded byte.  See _encodable for the character set.
-        encoding = win.encoding
+        # A border or line character that fits a single cell byte reads back
+        # via instr() as that byte and via inch() as the cell value.
         for ch in ('é', '¤', '€', 'є'):
             try:
-                b = ch.encode(encoding)
+                b = ch.encode(win.encoding)
             except UnicodeEncodeError:
                 continue
             if len(b) != 1:
                 continue
+            v = self._char_code(ch)
             with self.subTest(ch=ch):
                 win.erase()
                 win.hline(2, 0, ch, 5)
                 self.assertEqual(win.instr(2, 0, 5), b * 5)
+                self.assertEqual(win.inch(2, 0) & curses.A_CHARTEXT, v)
                 win.vline(0, 0, ch, 3)
                 self.assertEqual(win.instr(0, 0, 1), b)
                 self.assertEqual(win.instr(1, 0, 1), b)
@@ -1510,7 +1511,6 @@ class TestCurses(unittest.TestCase):
                 if ord(ch) < 0x100:
                     # The same byte given as an int.  A wide build stores it
                     # through the locale, so only a Latin-1 byte round-trips.
-                    v = b[0]
                     win.erase()
                     win.hline(2, 0, v, 5)
                     self.assertEqual(win.instr(2, 0, 5), b * 5)
@@ -2894,12 +2894,11 @@ class TextboxTest(unittest.TestCase):
     def test_insert(self):
         """Test inserting a printable character."""
         self.mock_win.reset_mock()
-        self.textbox.do_command(ord('a'))
-        self.mock_win.addch.assert_called_with(ord('a'))
-        self.textbox.do_command(ord('b'))
-        self.mock_win.addch.assert_called_with(ord('b'))
-        self.textbox.do_command(ord('c'))
-        self.mock_win.addch.assert_called_with(ord('c'))
+        # An integer keystroke is decoded to text: addch() would take it as a
+        # code point on a wide build.
+        for ch in 'abc':
+            self.textbox.do_command(ord(ch))
+            self.mock_win.addch.assert_called_with(ch, 0)
         self.mock_win.reset_mock()
 
     def test_delete(self):
index 132b55c0a500fb6ec2bbf5082cb56d788d543bae..f9ac5f0654f5e9e1c90ef4dba1508d3e3f1b64a1 100644 (file)
@@ -463,7 +463,7 @@ curses_window_check_err(PyCursesWindowObject *win, int code,
 static int
 PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch)
 {
-    long value;
+    unsigned long long value;
     if (PyBytes_Check(obj)) {
         if (PyBytes_GET_SIZE(obj) != 1) {
             PyErr_Format(PyExc_TypeError,
@@ -493,20 +493,18 @@ PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch)
             bytes = PyUnicode_AsEncodedString(obj, encoding, NULL);
             if (bytes == NULL)
                 return 0;
-            if (PyBytes_GET_SIZE(bytes) == 1)
-                value = (unsigned char)PyBytes_AS_STRING(bytes)[0];
-            else
-                value = -1;
-            Py_DECREF(bytes);
-            if (value < 0)
+            if (PyBytes_GET_SIZE(bytes) != 1) {
+                Py_DECREF(bytes);
                 goto overflow;
+            }
+            value = (unsigned char)PyBytes_AS_STRING(bytes)[0];
+            Py_DECREF(bytes);
         }
     }
     else if (PyLong_CheckExact(obj)) {
-        int long_overflow;
-        value = PyLong_AsLongAndOverflow(obj, &long_overflow);
-        if (long_overflow)
-            goto overflow;
+        value = PyLong_AsUnsignedLongLong(obj);
+        if (value == (unsigned long long)-1 && PyErr_Occurred())
+            return 0;
     }
     else {
         PyErr_Format(PyExc_TypeError,
@@ -515,7 +513,7 @@ PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch)
         return 0;
     }
     *ch = (chtype)value;
-    if ((long)*ch != value)
+    if ((unsigned long long)*ch != value)
         goto overflow;
     return 1;
 
@@ -587,7 +585,7 @@ PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj,
 #endif
                           )
 {
-    long value;
+    unsigned long long value;
 
     if (PyUnicode_Check(obj)) {
 #ifdef HAVE_NCURSESW
@@ -610,11 +608,8 @@ PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj,
         value = (unsigned char)PyBytes_AsString(obj)[0];
     }
     else if (PyLong_CheckExact(obj)) {
-        int overflow;
-        value = PyLong_AsLongAndOverflow(obj, &overflow);
-        if (overflow) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "int doesn't fit in long");
+        value = PyLong_AsUnsignedLongLong(obj);
+        if (value == (unsigned long long)-1 && PyErr_Occurred()) {
             return 0;
         }
     }
@@ -626,7 +621,7 @@ PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj,
     }
 
     *ch = (chtype)value;
-    if ((long)*ch != value) {
+    if ((unsigned long long)*ch != value) {
         PyErr_Format(PyExc_OverflowError,
                      "byte doesn't fit in chtype");
         return 0;
@@ -1082,14 +1077,14 @@ attr_converter(PyObject *arg, void *ptr)
 {
     /* attr_t is unsigned and at least as wide as chtype, so an attribute
        value must be a non-negative integer that fits in attr_t. */
-    unsigned long attr = PyLong_AsUnsignedLong(arg);
-    if (attr == (unsigned long)-1 && PyErr_Occurred()) {
+    unsigned long long attr = PyLong_AsUnsignedLongLong(arg);
+    if (attr == (unsigned long long)-1 && PyErr_Occurred()) {
         return 0;
     }
-    if (attr > (unsigned long)(attr_t)-1) {
+    if (attr > (unsigned long long)(attr_t)-1) {
         PyErr_Format(PyExc_OverflowError,
-                     "attribute value is greater than maximum (%lu)",
-                     (unsigned long)(attr_t)-1);
+                     "attribute value is greater than maximum (%llu)",
+                     (unsigned long long)(attr_t)-1);
         return 0;
     }
     *(attr_t *)ptr = (attr_t)attr;
@@ -1224,7 +1219,7 @@ complexchar_get_attr(PyObject *self, void *Py_UNUSED(closure))
             &_PyCursesComplexCharObject_CAST(self)->cval, &attrs, &pair) < 0) {
         return NULL;
     }
-    return PyLong_FromUnsignedLong((unsigned long)attrs);
+    return PyLong_FromUnsignedLongLong((unsigned long long)attrs);
 }
 
 static PyObject *
@@ -2603,7 +2598,7 @@ static PyObject *
 _curses_window_getattrs_impl(PyCursesWindowObject *self)
 /*[clinic end generated code: output=835f499205204ec4 input=bf56a0af5b730bd1]*/
 {
-    return PyLong_FromUnsignedLong((unsigned long)(attr_t)getattrs(self->win));
+    return PyLong_FromUnsignedLongLong((unsigned long long)(attr_t)getattrs(self->win));
 }
 
 /*[clinic input]
@@ -3142,7 +3137,7 @@ _curses_window_getbkgd_impl(PyCursesWindowObject *self)
         curses_window_set_error(self, "getbkgd", NULL);
         return NULL;
     }
-    return PyLong_FromLong(rtn);
+    return PyLong_FromUnsignedLongLong(rtn);
 }
 
 /*[clinic input]
@@ -3713,9 +3708,8 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
     chtype rtn;
     const char *funcname;
 #ifdef HAVE_NCURSESW
-    /* 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; read the wide cell and rebuild it instead. */
+    /* ncursesw's winch() returns the whole code point, which overflows the
+       chtype's 8-bit character field into the color and attribute bits. */
     cchar_t cell = {0};
     int rc;
     if (!group_right_1) {
@@ -3759,7 +3753,7 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
         return NULL;
     }
 #endif
-    return PyLong_FromUnsignedLong(rtn);
+    return PyLong_FromUnsignedLongLong(rtn);
 }
 
 PyDoc_STRVAR(_curses_window_instr__doc__,
@@ -5776,7 +5770,7 @@ _curses_color_pair_impl(PyObject *module, int pair_number)
                      pair_number, (int)PAIR_NUMBER(A_COLOR));
         return NULL;
     }
-    return PyLong_FromLong(attr);
+    return PyLong_FromUnsignedLongLong(attr);
 }
 
 /*[clinic input]
@@ -6562,9 +6556,14 @@ 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). */
 #define SetDictInt(NAME, VALUE)                                     \
     do {                                                            \
-        PyObject *value = PyLong_FromLong((long)(VALUE));           \
+        PyObject *value = PyLong_FromUnsignedLongLong((unsigned long long)(VALUE));  \
         if (value == NULL) {                                        \
             return -1;                                              \
         }                                                           \
@@ -7531,7 +7530,7 @@ _curses_pair_content_impl(PyObject *module, int pair_number)
 @permit_long_summary
 _curses.pair_number
 
-    attr: int
+    attr: attr
     /
 
 Return the number of the color-pair set by the specified attribute value.
@@ -7540,8 +7539,8 @@ color_pair() is the counterpart to this function.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_pair_number_impl(PyObject *module, int attr)
-/*[clinic end generated code: output=85bce7d65c0aa3f4 input=b11152a78c2f9abf]*/
+_curses_pair_number_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=04cafc9083329197 input=562273b1a12a06d2]*/
 {
     PyCursesStatefulInitialised(module);
     PyCursesStatefulInitialisedColor(module);
@@ -7872,7 +7871,7 @@ _curses_start_color_impl(PyObject *module)
     }
 #define DICT_ADD_INT_VALUE(NAME, VALUE)                             \
     do {                                                            \
-        PyObject *value = PyLong_FromLong((long)(VALUE));           \
+        PyObject *value = PyLong_FromUnsignedLongLong((unsigned long long)(VALUE));  \
         if (value == NULL) {                                        \
             return NULL;                                            \
         }                                                           \
@@ -7917,7 +7916,7 @@ _curses_term_attrs_impl(PyObject *module)
 {
     PyCursesStatefulInitialised(module);
 
-    return PyLong_FromUnsignedLong(term_attrs());
+    return PyLong_FromUnsignedLongLong(term_attrs());
 }
 #endif /* HAVE_CURSES_TERM_ATTRS */
 
@@ -8437,15 +8436,15 @@ NoArgNoReturnFunctionBody(slk_touch)
 /*[clinic input]
 _curses.slk_attron
 
-    attr: long
+    attr: attr
     /
 
 Add the given chtype attributes to the soft labels.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_slk_attron_impl(PyObject *module, long attr)
-/*[clinic end generated code: output=01aa29848a58ab50 input=fa198a604e3eec04]*/
+_curses_slk_attron_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=c2a4bfac8ddbbf20 input=c2cfffeb7ce6a86e]*/
 {
     PyCursesStatefulInitialised(module);
     return curses_check_err(module, slk_attron((chtype)attr),
@@ -8455,15 +8454,15 @@ _curses_slk_attron_impl(PyObject *module, long attr)
 /*[clinic input]
 _curses.slk_attroff
 
-    attr: long
+    attr: attr
     /
 
 Remove the given chtype attributes from the soft labels.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_slk_attroff_impl(PyObject *module, long attr)
-/*[clinic end generated code: output=7b172cc37a17811f input=21dab55d43d30b8f]*/
+_curses_slk_attroff_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=321a3b4dc61119bb input=2593027e03cbd302]*/
 {
     PyCursesStatefulInitialised(module);
     return curses_check_err(module, slk_attroff((chtype)attr),
@@ -8473,15 +8472,15 @@ _curses_slk_attroff_impl(PyObject *module, long attr)
 /*[clinic input]
 _curses.slk_attrset
 
-    attr: long
+    attr: attr
     /
 
 Set the chtype attributes of the soft labels.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_slk_attrset_impl(PyObject *module, long attr)
-/*[clinic end generated code: output=1139e2b0f757edfd input=d5c798956a5f046a]*/
+_curses_slk_attrset_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=f396b745cb23fc01 input=5faafda445b2b158]*/
 {
     PyCursesStatefulInitialised(module);
     return curses_check_err(module, slk_attrset((chtype)attr),
@@ -8500,7 +8499,7 @@ _curses_slk_attr_impl(PyObject *module)
 /*[clinic end generated code: output=6d47752f82bdc29f input=be38805fdec52149]*/
 {
     PyCursesStatefulInitialised(module);
-    return PyLong_FromUnsignedLong((unsigned long)slk_attr());
+    return PyLong_FromUnsignedLongLong((unsigned long long)slk_attr());
 }
 #endif
 
@@ -9080,6 +9079,17 @@ cursesmodule_exec(PyObject *module)
         return -1;
     }
 
+    /* Whether a cell holds a character or a single byte of the locale
+       encoding, decided when the module is built. */
+#ifdef HAVE_NCURSESW
+    rc = PyDict_SetItemString(module_dict, "_wide_character_support", Py_True);
+#else
+    rc = PyDict_SetItemString(module_dict, "_wide_character_support", Py_False);
+#endif
+    if (rc < 0) {
+        return -1;
+    }
+
     /* Make the version available */
     PyObject *curses_version = PyBytes_FromString(PyCursesVersion);
     if (curses_version == NULL) {
@@ -9118,7 +9128,7 @@ cursesmodule_exec(PyObject *module)
 
 #define SetDictInt(NAME, VALUE)                                     \
     do {                                                            \
-        PyObject *value = PyLong_FromLong((long)(VALUE));           \
+        PyObject *value = PyLong_FromUnsignedLongLong((unsigned long long)(VALUE));  \
         if (value == NULL) {                                        \
             return -1;                                              \
         }                                                           \
index 70d99b4bf351a21aa22905c924eaee4366e625fc..dfd589ba45089e79e5ffdca7ecb37966553eb159 100644 (file)
@@ -4684,16 +4684,15 @@ PyDoc_STRVAR(_curses_pair_number__doc__,
     {"pair_number", (PyCFunction)_curses_pair_number, METH_O, _curses_pair_number__doc__},
 
 static PyObject *
-_curses_pair_number_impl(PyObject *module, int attr);
+_curses_pair_number_impl(PyObject *module, attr_t attr);
 
 static PyObject *
 _curses_pair_number(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    int attr;
+    attr_t attr;
 
-    attr = PyLong_AsInt(arg);
-    if (attr == -1 && PyErr_Occurred()) {
+    if (!attr_converter(arg, &attr)) {
         goto exit;
     }
     return_value = _curses_pair_number_impl(module, attr);
@@ -5675,16 +5674,15 @@ PyDoc_STRVAR(_curses_slk_attron__doc__,
     {"slk_attron", (PyCFunction)_curses_slk_attron, METH_O, _curses_slk_attron__doc__},
 
 static PyObject *
-_curses_slk_attron_impl(PyObject *module, long attr);
+_curses_slk_attron_impl(PyObject *module, attr_t attr);
 
 static PyObject *
 _curses_slk_attron(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    long attr;
+    attr_t attr;
 
-    attr = PyLong_AsLong(arg);
-    if (attr == -1 && PyErr_Occurred()) {
+    if (!attr_converter(arg, &attr)) {
         goto exit;
     }
     return_value = _curses_slk_attron_impl(module, attr);
@@ -5703,16 +5701,15 @@ PyDoc_STRVAR(_curses_slk_attroff__doc__,
     {"slk_attroff", (PyCFunction)_curses_slk_attroff, METH_O, _curses_slk_attroff__doc__},
 
 static PyObject *
-_curses_slk_attroff_impl(PyObject *module, long attr);
+_curses_slk_attroff_impl(PyObject *module, attr_t attr);
 
 static PyObject *
 _curses_slk_attroff(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    long attr;
+    attr_t attr;
 
-    attr = PyLong_AsLong(arg);
-    if (attr == -1 && PyErr_Occurred()) {
+    if (!attr_converter(arg, &attr)) {
         goto exit;
     }
     return_value = _curses_slk_attroff_impl(module, attr);
@@ -5731,16 +5728,15 @@ PyDoc_STRVAR(_curses_slk_attrset__doc__,
     {"slk_attrset", (PyCFunction)_curses_slk_attrset, METH_O, _curses_slk_attrset__doc__},
 
 static PyObject *
-_curses_slk_attrset_impl(PyObject *module, long attr);
+_curses_slk_attrset_impl(PyObject *module, attr_t attr);
 
 static PyObject *
 _curses_slk_attrset(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    long attr;
+    attr_t attr;
 
-    attr = PyLong_AsLong(arg);
-    if (attr == -1 && PyErr_Occurred()) {
+    if (!attr_converter(arg, &attr)) {
         goto exit;
     }
     return_value = _curses_slk_attrset_impl(module, attr);
@@ -6238,4 +6234,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored
 #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
     #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
 #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */
-/*[clinic end generated code: output=ae1359964feefd64 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=cb5525c88ae5c440 input=a9049054013a1b77]*/