Skip to content

Commit b232df9

Browse files
bpo-31680: Add curses.ncurses_version. (GH-4217)
Use curses.ncurses_version for conditionally skipping a test.
1 parent 3e429dc commit b232df9

File tree

5 files changed

+119
-3
lines changed

5 files changed

+119
-3
lines changed

Doc/library/curses.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,19 @@ The :mod:`curses` module defines the following data members:
12911291
A bytes object representing the current version of the module. Also available as
12921292
:const:`__version__`.
12931293

1294+
1295+
.. data:: ncurses_version
1296+
1297+
A named tuple containing the three components of the ncurses library
1298+
version: *major*, *minor*, and *patch*. All values are integers. The
1299+
components can also be accessed by name, so ``curses.ncurses_version[0]``
1300+
is equivalent to ``curses.ncurses_version.major`` and so on.
1301+
1302+
Availability: if the ncurses library is used.
1303+
1304+
.. versionadded:: 3.8
1305+
1306+
12941307
Some constants are available to specify character cell attributes.
12951308
The exact constants available are system dependent.
12961309

Doc/whatsnew/3.8.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ now return ``False`` instead of raising :exc:`ValueError` or its subclasses
152152
characters or bytes unrepresentable at the OS level.
153153
(Contributed by Serhiy Storchaka in :issue:`33721`.)
154154

155+
156+
ncurses
157+
-------
158+
159+
Added a new variable holding structured version information for the
160+
underlying ncurses library: :data:`~curses.ncurses_version`.
161+
(Contributed by Serhiy Storchaka in :issue:`31680`.)
162+
163+
155164
pathlib
156165
-------
157166

Lib/test/test_curses.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,8 @@ def test_issue6243(self):
368368
self.stdscr.getkey()
369369

370370
@requires_curses_func('unget_wch')
371-
# XXX Remove the decorator when ncurses on OpenBSD be updated
372-
@unittest.skipIf(sys.platform.startswith("openbsd"),
373-
"OpenBSD's curses (v.5.7) has bugs")
371+
@unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
372+
"unget_wch is broken in ncurses 5.7 and earlier")
374373
def test_unget_wch(self):
375374
stdscr = self.stdscr
376375
encoding = stdscr.encoding
@@ -456,6 +455,23 @@ def test_update_lines_cols(self):
456455
# can be called.
457456
curses.update_lines_cols()
458457

458+
@requires_curses_func('ncurses_version')
459+
def test_ncurses_version(self):
460+
v = curses.ncurses_version
461+
self.assertIsInstance(v[:], tuple)
462+
self.assertEqual(len(v), 3)
463+
self.assertIsInstance(v[0], int)
464+
self.assertIsInstance(v[1], int)
465+
self.assertIsInstance(v[2], int)
466+
self.assertIsInstance(v.major, int)
467+
self.assertIsInstance(v.minor, int)
468+
self.assertIsInstance(v.patch, int)
469+
self.assertEqual(v[0], v.major)
470+
self.assertEqual(v[1], v.minor)
471+
self.assertEqual(v[2], v.patch)
472+
self.assertGreaterEqual(v.major, 0)
473+
self.assertGreaterEqual(v.minor, 0)
474+
self.assertGreaterEqual(v.patch, 0)
459475

460476
class TestAscii(unittest.TestCase):
461477

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added :data:`curses.ncurses_version`.

Modules/_cursesmodule.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4289,6 +4289,59 @@ _curses_use_default_colors_impl(PyObject *module)
42894289
}
42904290
#endif /* STRICT_SYSV_CURSES */
42914291

4292+
4293+
#ifdef NCURSES_VERSION
4294+
4295+
PyDoc_STRVAR(ncurses_version__doc__,
4296+
"curses.ncurses_version\n\
4297+
\n\
4298+
Ncurses version information as a named tuple.");
4299+
4300+
static PyTypeObject NcursesVersionType;
4301+
4302+
static PyStructSequence_Field ncurses_version_fields[] = {
4303+
{"major", "Major release number"},
4304+
{"minor", "Minor release number"},
4305+
{"patch", "Patch release number"},
4306+
{0}
4307+
};
4308+
4309+
static PyStructSequence_Desc ncurses_version_desc = {
4310+
"curses.ncurses_version", /* name */
4311+
ncurses_version__doc__, /* doc */
4312+
ncurses_version_fields, /* fields */
4313+
3
4314+
};
4315+
4316+
static PyObject *
4317+
make_ncurses_version(void)
4318+
{
4319+
PyObject *ncurses_version;
4320+
int pos = 0;
4321+
4322+
ncurses_version = PyStructSequence_New(&NcursesVersionType);
4323+
if (ncurses_version == NULL) {
4324+
return NULL;
4325+
}
4326+
4327+
#define SetIntItem(flag) \
4328+
PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \
4329+
if (PyErr_Occurred()) { \
4330+
Py_CLEAR(ncurses_version); \
4331+
return NULL; \
4332+
}
4333+
4334+
SetIntItem(NCURSES_VERSION_MAJOR)
4335+
SetIntItem(NCURSES_VERSION_MINOR)
4336+
SetIntItem(NCURSES_VERSION_PATCH)
4337+
#undef SetIntItem
4338+
4339+
return ncurses_version;
4340+
}
4341+
4342+
#endif /* NCURSES_VERSION */
4343+
4344+
42924345
/* List of functions defined in the module */
42934346

42944347
static PyMethodDef PyCurses_methods[] = {
@@ -4426,6 +4479,30 @@ PyInit__curses(void)
44264479
PyDict_SetItemString(d, "__version__", v);
44274480
Py_DECREF(v);
44284481

4482+
#ifdef NCURSES_VERSION
4483+
/* ncurses_version */
4484+
if (NcursesVersionType.tp_name == NULL) {
4485+
if (PyStructSequence_InitType2(&NcursesVersionType,
4486+
&ncurses_version_desc) < 0)
4487+
return NULL;
4488+
}
4489+
v = make_ncurses_version();
4490+
if (v == NULL) {
4491+
return NULL;
4492+
}
4493+
PyDict_SetItemString(d, "ncurses_version", v);
4494+
Py_DECREF(v);
4495+
4496+
/* prevent user from creating new instances */
4497+
NcursesVersionType.tp_init = NULL;
4498+
NcursesVersionType.tp_new = NULL;
4499+
if (PyDict_DelItemString(NcursesVersionType.tp_dict, "__new__") < 0 &&
4500+
PyErr_ExceptionMatches(PyExc_KeyError))
4501+
{
4502+
PyErr_Clear();
4503+
}
4504+
#endif /* NCURSES_VERSION */
4505+
44294506
SetDictInt("ERR", ERR);
44304507
SetDictInt("OK", OK);
44314508

0 commit comments

Comments
 (0)