bpo-42681: Fix test_curses failures related to color pairs (GH-24089)
On ncurses 6.1 pair numbers are limited by SHORT_MAX-1, even with extended color support. Improve error reporting and tests for color functions.
This commit is contained in:
parent
27f9dafc2b
commit
59f9b4e450
|
@ -47,6 +47,7 @@ def requires_colors(test):
|
|||
return wrapped
|
||||
|
||||
term = os.environ.get('TERM')
|
||||
SHORT_MAX = 0x7fff
|
||||
|
||||
# If newterm was supported we could use it instead of initscr and not exit
|
||||
@unittest.skipIf(not term or term == 'unknown',
|
||||
|
@ -327,11 +328,20 @@ class TestCurses(unittest.TestCase):
|
|||
def bad_pairs(self):
|
||||
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
||||
|
||||
def test_start_color(self):
|
||||
if not curses.has_colors():
|
||||
self.skipTest('requires colors support')
|
||||
curses.start_color()
|
||||
if verbose:
|
||||
print(f'COLORS = {curses.COLORS}', file=sys.stderr)
|
||||
print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
|
||||
|
||||
@requires_colors
|
||||
def test_color_content(self):
|
||||
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
||||
curses.color_content(0)
|
||||
curses.color_content(curses.COLORS - 1)
|
||||
maxcolor = curses.COLORS - 1
|
||||
curses.color_content(maxcolor)
|
||||
|
||||
for color in self.bad_colors():
|
||||
self.assertRaises(ValueError, curses.color_content, color)
|
||||
|
@ -352,11 +362,12 @@ class TestCurses(unittest.TestCase):
|
|||
curses.init_color(0, 1000, 1000, 1000)
|
||||
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
||||
|
||||
old = curses.color_content(curses.COLORS - 1)
|
||||
curses.init_color(curses.COLORS - 1, *old)
|
||||
self.addCleanup(curses.init_color, curses.COLORS - 1, *old)
|
||||
curses.init_color(curses.COLORS - 1, 0, 500, 1000)
|
||||
self.assertEqual(curses.color_content(curses.COLORS - 1), (0, 500, 1000))
|
||||
maxcolor = curses.COLORS - 1
|
||||
old = curses.color_content(maxcolor)
|
||||
curses.init_color(maxcolor, *old)
|
||||
self.addCleanup(curses.init_color, maxcolor, *old)
|
||||
curses.init_color(maxcolor, 0, 500, 1000)
|
||||
self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
|
||||
|
||||
for color in self.bad_colors():
|
||||
self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
|
||||
|
@ -365,13 +376,25 @@ class TestCurses(unittest.TestCase):
|
|||
self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
|
||||
self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
|
||||
|
||||
def get_pair_limit(self):
|
||||
pair_limit = curses.COLOR_PAIRS
|
||||
if hasattr(curses, 'ncurses_version'):
|
||||
if curses.has_extended_color_support():
|
||||
pair_limit += 2*curses.COLORS + 1
|
||||
if (not curses.has_extended_color_support()
|
||||
or (6, 1) <= curses.ncurses_version < (6, 2)):
|
||||
pair_limit = min(pair_limit, SHORT_MAX)
|
||||
return pair_limit
|
||||
|
||||
@requires_colors
|
||||
def test_pair_content(self):
|
||||
if not hasattr(curses, 'use_default_colors'):
|
||||
self.assertEqual(curses.pair_content(0),
|
||||
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
||||
curses.pair_content(0)
|
||||
curses.pair_content(curses.COLOR_PAIRS - 1)
|
||||
maxpair = self.get_pair_limit() - 1
|
||||
if maxpair > 0:
|
||||
curses.pair_content(maxpair)
|
||||
|
||||
for pair in self.bad_pairs():
|
||||
self.assertRaises(ValueError, curses.pair_content, pair)
|
||||
|
@ -384,11 +407,15 @@ class TestCurses(unittest.TestCase):
|
|||
|
||||
curses.init_pair(1, 0, 0)
|
||||
self.assertEqual(curses.pair_content(1), (0, 0))
|
||||
curses.init_pair(1, curses.COLORS - 1, curses.COLORS - 1)
|
||||
self.assertEqual(curses.pair_content(1),
|
||||
(curses.COLORS - 1, curses.COLORS - 1))
|
||||
curses.init_pair(curses.COLOR_PAIRS - 1, 2, 3)
|
||||
self.assertEqual(curses.pair_content(curses.COLOR_PAIRS - 1), (2, 3))
|
||||
maxcolor = curses.COLORS - 1
|
||||
curses.init_pair(1, maxcolor, 0)
|
||||
self.assertEqual(curses.pair_content(1), (maxcolor, 0))
|
||||
curses.init_pair(1, 0, maxcolor)
|
||||
self.assertEqual(curses.pair_content(1), (0, maxcolor))
|
||||
maxpair = self.get_pair_limit() - 1
|
||||
if maxpair > 1:
|
||||
curses.init_pair(maxpair, 0, 0)
|
||||
self.assertEqual(curses.pair_content(maxpair), (0, 0))
|
||||
|
||||
for pair in self.bad_pairs():
|
||||
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
||||
|
@ -582,6 +609,8 @@ class MiscTests(unittest.TestCase):
|
|||
@requires_curses_func('ncurses_version')
|
||||
def test_ncurses_version(self):
|
||||
v = curses.ncurses_version
|
||||
if verbose:
|
||||
print(f'ncurses_version = {curses.ncurses_version}', flush=True)
|
||||
self.assertIsInstance(v[:], tuple)
|
||||
self.assertEqual(len(v), 3)
|
||||
self.assertIsInstance(v[0], int)
|
||||
|
|
|
@ -135,29 +135,28 @@ typedef chtype attr_t; /* No attr_t type is available */
|
|||
#define STRICT_SYSV_CURSES
|
||||
#endif
|
||||
|
||||
#if defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS)
|
||||
#if NCURSES_EXT_COLORS+0 && NCURSES_EXT_FUNCS+0
|
||||
#define _NCURSES_EXTENDED_COLOR_FUNCS 1
|
||||
#else
|
||||
#define _NCURSES_EXTENDED_COLOR_FUNCS 0
|
||||
#endif /* defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) */
|
||||
|
||||
#if _NCURSES_EXTENDED_COLOR_FUNCS
|
||||
#define _NCURSES_COLOR_VAL_TYPE int
|
||||
#define _CURSES_COLOR_VAL_TYPE int
|
||||
#define _CURSES_COLOR_NUM_TYPE int
|
||||
#define _CURSES_INIT_COLOR_FUNC init_extended_color
|
||||
#define _CURSES_INIT_PAIR_FUNC init_extended_pair
|
||||
#define _COLOR_CONTENT_FUNC extended_color_content
|
||||
#define _CURSES_PAIR_NUMBER_FUNC extended_pair_content
|
||||
#define _CURSES_PAIR_CONTENT_FUNC extended_pair_content
|
||||
#else
|
||||
#define _NCURSES_COLOR_VAL_TYPE short
|
||||
#define _CURSES_COLOR_VAL_TYPE short
|
||||
#define _CURSES_COLOR_NUM_TYPE short
|
||||
#define _CURSES_INIT_COLOR_FUNC init_color
|
||||
#define _CURSES_INIT_PAIR_FUNC init_pair
|
||||
#define _COLOR_CONTENT_FUNC color_content
|
||||
#define _CURSES_PAIR_NUMBER_FUNC pair_content
|
||||
#define _CURSES_PAIR_CONTENT_FUNC pair_content
|
||||
#endif /* _NCURSES_EXTENDED_COLOR_FUNCS */
|
||||
|
||||
#define _CURSES_INIT_COLOR_FUNC_NAME Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC)
|
||||
#define _CURSES_INIT_PAIR_FUNC_NAME Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC)
|
||||
|
||||
/*[clinic input]
|
||||
module _curses
|
||||
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
|
||||
|
@ -2737,18 +2736,18 @@ static PyObject *
|
|||
_curses_color_content_impl(PyObject *module, int color_number)
|
||||
/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/
|
||||
{
|
||||
_NCURSES_COLOR_VAL_TYPE r,g,b;
|
||||
_CURSES_COLOR_VAL_TYPE r,g,b;
|
||||
|
||||
PyCursesInitialised;
|
||||
PyCursesInitialisedColor;
|
||||
|
||||
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) != ERR)
|
||||
return Py_BuildValue("(iii)", r, g, b);
|
||||
else {
|
||||
PyErr_SetString(PyCursesError,
|
||||
"Argument 1 was out of range. Check value of COLORS.");
|
||||
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) == ERR) {
|
||||
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||
Py_STRINGIFY(_COLOR_CONTENT_FUNC));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return Py_BuildValue("(iii)", r, g, b);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
@ -3190,7 +3189,8 @@ _curses_init_color_impl(PyObject *module, int color_number, short r, short g,
|
|||
PyCursesInitialised;
|
||||
PyCursesInitialisedColor;
|
||||
|
||||
return PyCursesCheckERR(_CURSES_INIT_COLOR_FUNC(color_number, r, g, b), _CURSES_INIT_COLOR_FUNC_NAME);
|
||||
return PyCursesCheckERR(_CURSES_INIT_COLOR_FUNC(color_number, r, g, b),
|
||||
Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC));
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
@ -3217,7 +3217,20 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
|
|||
PyCursesInitialised;
|
||||
PyCursesInitialisedColor;
|
||||
|
||||
return PyCursesCheckERR(_CURSES_INIT_PAIR_FUNC(pair_number, fg, bg), _CURSES_INIT_PAIR_FUNC_NAME);
|
||||
if (_CURSES_INIT_PAIR_FUNC(pair_number, fg, bg) == ERR) {
|
||||
if (pair_number >= COLOR_PAIRS) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Color pair is greater than COLOR_PAIRS-1 (%d).",
|
||||
COLOR_PAIRS - 1);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||
Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *ModDict;
|
||||
|
@ -3845,14 +3858,21 @@ static PyObject *
|
|||
_curses_pair_content_impl(PyObject *module, int pair_number)
|
||||
/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/
|
||||
{
|
||||
_NCURSES_COLOR_VAL_TYPE f, b;
|
||||
_CURSES_COLOR_NUM_TYPE f, b;
|
||||
|
||||
PyCursesInitialised;
|
||||
PyCursesInitialisedColor;
|
||||
|
||||
if (_CURSES_PAIR_NUMBER_FUNC(pair_number, &f, &b)==ERR) {
|
||||
PyErr_SetString(PyCursesError,
|
||||
"Argument 1 was out of range. (0..COLOR_PAIRS-1)");
|
||||
if (_CURSES_PAIR_CONTENT_FUNC(pair_number, &f, &b) == ERR) {
|
||||
if (pair_number >= COLOR_PAIRS) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Color pair is greater than COLOR_PAIRS-1 (%d).",
|
||||
COLOR_PAIRS - 1);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||
Py_STRINGIFY(_CURSES_PAIR_CONTENT_FUNC));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue