Skip to content

Commit fe71527

Browse files
committed
gh-154874: Fix the sign of curses.termattrs()
termattrs() returns a chtype mask, but it was routed through an int, so a terminal that advertises A_ITALIC came back negative and the result could no longer be passed to the attribute functions.
1 parent 9ccd5bb commit fe71527

3 files changed

Lines changed: 36 additions & 1 deletion

File tree

Lib/test/test_curses.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3242,5 +3242,33 @@ def test_color(self):
32423242
curses.slk_color(0)
32433243

32443244

3245+
@unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
3246+
@unittest.skipIf(BROKEN_NEWTERM, 'ncurses < 6.5 mishandles repeated newterm()')
3247+
@unittest.skipIf(not term or term == 'unknown',
3248+
f"$TERM={term!r}, newterm() may not work")
3249+
@unittest.skipIf(sys.platform == "cygwin",
3250+
"cygwin's curses mostly just hangs")
3251+
class TermAttrsTests(NewtermTestBase):
3252+
# A_ITALIC is the topmost bit of a 32-bit attribute mask, so termattrs()
3253+
# only tells a signed result from an unsigned one on a terminal that
3254+
# advertises it. Drive a known terminal type over a pseudo-terminal
3255+
# instead of relying on whatever $TERM happens to be.
3256+
3257+
def test_termattrs_is_not_negative(self):
3258+
s = self.make_pty()
3259+
try:
3260+
curses.newterm('xterm-256color', s, s)
3261+
except curses.error:
3262+
self.skipTest('no xterm-256color terminfo entry')
3263+
attrs = curses.termattrs()
3264+
italic = getattr(curses, 'A_ITALIC', 0)
3265+
if not italic or not attrs & italic:
3266+
self.skipTest('the terminal advertises no attribute in the top bit')
3267+
self.assertGreaterEqual(attrs, 0)
3268+
# termattrs() exists to be passed back to the attribute functions,
3269+
# which reject a negative mask.
3270+
curses.newwin(1, 1).attrset(attrs)
3271+
3272+
32453273
if __name__ == '__main__':
32463274
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`curses.termattrs` returning a negative value on a terminal that
2+
supports :const:`curses.A_ITALIC`, which left its result unusable as an
3+
attribute mask.

Modules/_cursesmodule.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7899,7 +7899,11 @@ Return a logical OR of all video attributes supported by the terminal.
78997899
static PyObject *
79007900
_curses_termattrs_impl(PyObject *module)
79017901
/*[clinic end generated code: output=b06f437fce1b6fc4 input=0559882a04f84d1d]*/
7902-
NoArgReturnIntFunctionBody(termattrs)
7902+
{
7903+
PyCursesStatefulInitialised(module);
7904+
7905+
return PyLong_FromUnsignedLong((unsigned long)(chtype)termattrs());
7906+
}
79037907

79047908
#ifdef HAVE_CURSES_TERM_ATTRS
79057909
/*[clinic input]

0 commit comments

Comments
 (0)