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
|
return wrapped
|
||||||
|
|
||||||
term = os.environ.get('TERM')
|
term = os.environ.get('TERM')
|
||||||
|
SHORT_MAX = 0x7fff
|
||||||
|
|
||||||
# If newterm was supported we could use it instead of initscr and not exit
|
# If newterm was supported we could use it instead of initscr and not exit
|
||||||
@unittest.skipIf(not term or term == 'unknown',
|
@unittest.skipIf(not term or term == 'unknown',
|
||||||
|
@ -327,11 +328,20 @@ class TestCurses(unittest.TestCase):
|
||||||
def bad_pairs(self):
|
def bad_pairs(self):
|
||||||
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
|
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
|
@requires_colors
|
||||||
def test_color_content(self):
|
def test_color_content(self):
|
||||||
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
|
||||||
curses.color_content(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():
|
for color in self.bad_colors():
|
||||||
self.assertRaises(ValueError, curses.color_content, color)
|
self.assertRaises(ValueError, curses.color_content, color)
|
||||||
|
@ -352,11 +362,12 @@ class TestCurses(unittest.TestCase):
|
||||||
curses.init_color(0, 1000, 1000, 1000)
|
curses.init_color(0, 1000, 1000, 1000)
|
||||||
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
|
||||||
|
|
||||||
old = curses.color_content(curses.COLORS - 1)
|
maxcolor = curses.COLORS - 1
|
||||||
curses.init_color(curses.COLORS - 1, *old)
|
old = curses.color_content(maxcolor)
|
||||||
self.addCleanup(curses.init_color, curses.COLORS - 1, *old)
|
curses.init_color(maxcolor, *old)
|
||||||
curses.init_color(curses.COLORS - 1, 0, 500, 1000)
|
self.addCleanup(curses.init_color, maxcolor, *old)
|
||||||
self.assertEqual(curses.color_content(curses.COLORS - 1), (0, 500, 1000))
|
curses.init_color(maxcolor, 0, 500, 1000)
|
||||||
|
self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
|
||||||
|
|
||||||
for color in self.bad_colors():
|
for color in self.bad_colors():
|
||||||
self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
|
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, comp, 0)
|
||||||
self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
|
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
|
@requires_colors
|
||||||
def test_pair_content(self):
|
def test_pair_content(self):
|
||||||
if not hasattr(curses, 'use_default_colors'):
|
if not hasattr(curses, 'use_default_colors'):
|
||||||
self.assertEqual(curses.pair_content(0),
|
self.assertEqual(curses.pair_content(0),
|
||||||
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
(curses.COLOR_WHITE, curses.COLOR_BLACK))
|
||||||
curses.pair_content(0)
|
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():
|
for pair in self.bad_pairs():
|
||||||
self.assertRaises(ValueError, curses.pair_content, pair)
|
self.assertRaises(ValueError, curses.pair_content, pair)
|
||||||
|
@ -384,11 +407,15 @@ class TestCurses(unittest.TestCase):
|
||||||
|
|
||||||
curses.init_pair(1, 0, 0)
|
curses.init_pair(1, 0, 0)
|
||||||
self.assertEqual(curses.pair_content(1), (0, 0))
|
self.assertEqual(curses.pair_content(1), (0, 0))
|
||||||
curses.init_pair(1, curses.COLORS - 1, curses.COLORS - 1)
|
maxcolor = curses.COLORS - 1
|
||||||
self.assertEqual(curses.pair_content(1),
|
curses.init_pair(1, maxcolor, 0)
|
||||||
(curses.COLORS - 1, curses.COLORS - 1))
|
self.assertEqual(curses.pair_content(1), (maxcolor, 0))
|
||||||
curses.init_pair(curses.COLOR_PAIRS - 1, 2, 3)
|
curses.init_pair(1, 0, maxcolor)
|
||||||
self.assertEqual(curses.pair_content(curses.COLOR_PAIRS - 1), (2, 3))
|
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():
|
for pair in self.bad_pairs():
|
||||||
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
|
||||||
|
@ -582,6 +609,8 @@ class MiscTests(unittest.TestCase):
|
||||||
@requires_curses_func('ncurses_version')
|
@requires_curses_func('ncurses_version')
|
||||||
def test_ncurses_version(self):
|
def test_ncurses_version(self):
|
||||||
v = curses.ncurses_version
|
v = curses.ncurses_version
|
||||||
|
if verbose:
|
||||||
|
print(f'ncurses_version = {curses.ncurses_version}', flush=True)
|
||||||
self.assertIsInstance(v[:], tuple)
|
self.assertIsInstance(v[:], tuple)
|
||||||
self.assertEqual(len(v), 3)
|
self.assertEqual(len(v), 3)
|
||||||
self.assertIsInstance(v[0], int)
|
self.assertIsInstance(v[0], int)
|
||||||
|
|
|
@ -135,29 +135,28 @@ typedef chtype attr_t; /* No attr_t type is available */
|
||||||
#define STRICT_SYSV_CURSES
|
#define STRICT_SYSV_CURSES
|
||||||
#endif
|
#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
|
#define _NCURSES_EXTENDED_COLOR_FUNCS 1
|
||||||
#else
|
#else
|
||||||
#define _NCURSES_EXTENDED_COLOR_FUNCS 0
|
#define _NCURSES_EXTENDED_COLOR_FUNCS 0
|
||||||
#endif /* defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) */
|
#endif /* defined(NCURSES_EXT_COLORS) && defined(NCURSES_EXT_FUNCS) */
|
||||||
|
|
||||||
#if _NCURSES_EXTENDED_COLOR_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_COLOR_FUNC init_extended_color
|
||||||
#define _CURSES_INIT_PAIR_FUNC init_extended_pair
|
#define _CURSES_INIT_PAIR_FUNC init_extended_pair
|
||||||
#define _COLOR_CONTENT_FUNC extended_color_content
|
#define _COLOR_CONTENT_FUNC extended_color_content
|
||||||
#define _CURSES_PAIR_NUMBER_FUNC extended_pair_content
|
#define _CURSES_PAIR_CONTENT_FUNC extended_pair_content
|
||||||
#else
|
#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_COLOR_FUNC init_color
|
||||||
#define _CURSES_INIT_PAIR_FUNC init_pair
|
#define _CURSES_INIT_PAIR_FUNC init_pair
|
||||||
#define _COLOR_CONTENT_FUNC color_content
|
#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 */
|
#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]
|
/*[clinic input]
|
||||||
module _curses
|
module _curses
|
||||||
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
|
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
|
||||||
|
@ -2737,18 +2736,18 @@ static PyObject *
|
||||||
_curses_color_content_impl(PyObject *module, int color_number)
|
_curses_color_content_impl(PyObject *module, int color_number)
|
||||||
/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/
|
/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/
|
||||||
{
|
{
|
||||||
_NCURSES_COLOR_VAL_TYPE r,g,b;
|
_CURSES_COLOR_VAL_TYPE r,g,b;
|
||||||
|
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) != ERR)
|
if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) == ERR) {
|
||||||
return Py_BuildValue("(iii)", r, g, b);
|
PyErr_Format(PyCursesError, "%s() returned ERR",
|
||||||
else {
|
Py_STRINGIFY(_COLOR_CONTENT_FUNC));
|
||||||
PyErr_SetString(PyCursesError,
|
|
||||||
"Argument 1 was out of range. Check value of COLORS.");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Py_BuildValue("(iii)", r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -3190,7 +3189,8 @@ _curses_init_color_impl(PyObject *module, int color_number, short r, short g,
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
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]
|
/*[clinic input]
|
||||||
|
@ -3217,7 +3217,20 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
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;
|
static PyObject *ModDict;
|
||||||
|
@ -3845,14 +3858,21 @@ static PyObject *
|
||||||
_curses_pair_content_impl(PyObject *module, int pair_number)
|
_curses_pair_content_impl(PyObject *module, int pair_number)
|
||||||
/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/
|
/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/
|
||||||
{
|
{
|
||||||
_NCURSES_COLOR_VAL_TYPE f, b;
|
_CURSES_COLOR_NUM_TYPE f, b;
|
||||||
|
|
||||||
PyCursesInitialised;
|
PyCursesInitialised;
|
||||||
PyCursesInitialisedColor;
|
PyCursesInitialisedColor;
|
||||||
|
|
||||||
if (_CURSES_PAIR_NUMBER_FUNC(pair_number, &f, &b)==ERR) {
|
if (_CURSES_PAIR_CONTENT_FUNC(pair_number, &f, &b) == ERR) {
|
||||||
PyErr_SetString(PyCursesError,
|
if (pair_number >= COLOR_PAIRS) {
|
||||||
"Argument 1 was out of range. (0..COLOR_PAIRS-1)");
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue