diff --git a/Lib/curses/textpad.py b/Lib/curses/textpad.py index 70fa2c25f646328..f6dfc990901d995 100644 --- a/Lib/curses/textpad.py +++ b/Lib/curses/textpad.py @@ -57,14 +57,17 @@ def _update_max_yx(self): 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 @@ def _insert_printable_char(self, ch): 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 diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 4ba210c162d0f6d..845b9a135a24243 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -46,22 +46,7 @@ def wrapped(self, *args, **kwargs): 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 @@ def _storable(self, s): 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 @@ def test_output_character(self): 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 @@ def test_read_from_window(self): 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 @@ def test_getch(self): 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 @@ def test_background(self): 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 @@ def test_borders_and_lines(self): 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 @@ def test_borders_and_lines(self): 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 @@ def test_init(self): 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): diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 132b55c0a500fb6..f9ac5f0654f5e9e 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -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; \ } \ diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 70d99b4bf351a21..dfd589ba45089e7 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -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]*/