Skip to content

Commit 0635e55

Browse files
gh-152502: Detect optional curses functions with configure probes (GH-152504)
Some curses functions were called unconditionally or gated only by the ncurses-specific NCURSES_EXT_FUNCS macro, which broke building the module against other curses implementations (narrow ncurses, NetBSD curses) even when they provided the function. Detect each with a configure capability probe and gate on HAVE_CURSES_*. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8ec36f1 commit 0635e55

7 files changed

Lines changed: 681 additions & 24 deletions

File tree

Lib/test/test_curses.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1563,7 +1563,8 @@ def test_env_queries(self):
15631563
self.assertIsInstance(curses.has_ic(), bool)
15641564
self.assertIsInstance(curses.has_il(), bool)
15651565
self.assertIsInstance(curses.termattrs(), int)
1566-
self.assertIsInstance(curses.term_attrs(), int)
1566+
if hasattr(curses, 'term_attrs'):
1567+
self.assertIsInstance(curses.term_attrs(), int)
15671568

15681569
c = curses.killchar()
15691570
self.assertIsInstance(c, bytes)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :mod:`curses` module now detects its optional functions with
2+
:program:`configure` capability probes instead of assuming ncurses, so it
3+
builds against narrow (non-wide) ncurses and other curses implementations such
4+
as NetBSD curses, exposing exactly the functions they provide.

Modules/_cursesmodule.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5960,6 +5960,7 @@ _curses_getwin(PyObject *module, PyObject *file)
59605960
return res;
59615961
}
59625962

5963+
#ifdef HAVE_CURSES_SCR_DUMP
59635964
/*[clinic input]
59645965
_curses.scr_dump
59655966
@@ -6030,6 +6031,7 @@ static PyObject *
60306031
_curses_scr_set(PyObject *module, PyObject *filename)
60316032
/*[clinic end generated code: output=6056fdec12c5935f input=d248c20543cc289b]*/
60326033
ScreenDumpFunctionBody(scr_set)
6034+
#endif /* HAVE_CURSES_SCR_DUMP */
60336035

60346036
/*[clinic input]
60356037
_curses.halfdelay
@@ -6108,7 +6110,7 @@ _curses_has_key_impl(PyObject *module, int key)
61086110
}
61096111
#endif
61106112

6111-
#if defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS
6113+
#ifdef HAVE_CURSES_DEFINE_KEY
61126114
/*[clinic input]
61136115
_curses.define_key
61146116
@@ -6134,7 +6136,9 @@ _curses_define_key_impl(PyObject *module, const char *definition,
61346136
return curses_check_err(module, define_key(definition, keycode),
61356137
"define_key", NULL);
61366138
}
6139+
#endif /* HAVE_CURSES_DEFINE_KEY */
61376140

6141+
#ifdef HAVE_CURSES_KEY_DEFINED
61386142
/*[clinic input]
61396143
_curses.key_defined
61406144
@@ -6156,7 +6160,9 @@ _curses_key_defined_impl(PyObject *module, const char *definition)
61566160

61576161
return PyLong_FromLong(key_defined(definition));
61586162
}
6163+
#endif /* HAVE_CURSES_KEY_DEFINED */
61596164

6165+
#ifdef HAVE_CURSES_KEYOK
61606166
/*[clinic input]
61616167
_curses.keyok
61626168
@@ -6177,7 +6183,7 @@ _curses_keyok_impl(PyObject *module, int keycode, int enable)
61776183

61786184
return curses_check_err(module, keyok(keycode, enable), "keyok", NULL);
61796185
}
6180-
#endif
6186+
#endif /* HAVE_CURSES_KEYOK */
61816187

61826188
/*[clinic input]
61836189
_curses.init_color
@@ -6778,9 +6784,7 @@ _curses_new_prescr_impl(PyObject *module)
67786784
}
67796785
#endif /* HAVE_CURSES_NEW_PRESCR */
67806786

6781-
#if defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20081102
6782-
// https://invisible-island.net/ncurses/NEWS.html#index-t20080119
6783-
6787+
#ifdef HAVE_CURSES_ESCDELAY
67846788
/*[clinic input]
67856789
_curses.get_escdelay
67866790
@@ -6797,6 +6801,9 @@ _curses_get_escdelay_impl(PyObject *module)
67976801
{
67986802
return PyLong_FromLong(ESCDELAY);
67996803
}
6804+
#endif /* HAVE_CURSES_ESCDELAY */
6805+
6806+
#ifdef HAVE_CURSES_SET_ESCDELAY
68006807
/*[clinic input]
68016808
_curses.set_escdelay
68026809
ms: int
@@ -6821,7 +6828,9 @@ _curses_set_escdelay_impl(PyObject *module, int ms)
68216828

68226829
return curses_check_err(module, set_escdelay(ms), "set_escdelay", NULL);
68236830
}
6831+
#endif /* HAVE_CURSES_SET_ESCDELAY */
68246832

6833+
#ifdef HAVE_CURSES_TABSIZE
68256834
/*[clinic input]
68266835
_curses.get_tabsize
68276836
@@ -6837,6 +6846,9 @@ _curses_get_tabsize_impl(PyObject *module)
68376846
{
68386847
return PyLong_FromLong(TABSIZE);
68396848
}
6849+
#endif /* HAVE_CURSES_TABSIZE */
6850+
6851+
#ifdef HAVE_CURSES_SET_TABSIZE
68406852
/*[clinic input]
68416853
_curses.set_tabsize
68426854
size: int
@@ -6860,7 +6872,7 @@ _curses_set_tabsize_impl(PyObject *module, int size)
68606872

68616873
return curses_check_err(module, set_tabsize(size), "set_tabsize", NULL);
68626874
}
6863-
#endif
6875+
#endif /* HAVE_CURSES_SET_TABSIZE */
68646876

68656877
/*[clinic input]
68666878
_curses.intrflush
@@ -7686,6 +7698,7 @@ _curses_termattrs_impl(PyObject *module)
76867698
/*[clinic end generated code: output=b06f437fce1b6fc4 input=0559882a04f84d1d]*/
76877699
NoArgReturnIntFunctionBody(termattrs)
76887700

7701+
#ifdef HAVE_CURSES_TERM_ATTRS
76897702
/*[clinic input]
76907703
_curses.term_attrs
76917704
@@ -7703,6 +7716,7 @@ _curses_term_attrs_impl(PyObject *module)
77037716

77047717
return PyLong_FromUnsignedLong(term_attrs());
77057718
}
7719+
#endif /* HAVE_CURSES_TERM_ATTRS */
77067720

77077721
/*[clinic input]
77087722
@permit_long_summary
@@ -8301,10 +8315,8 @@ static PyMethodDef cursesmodule_methods[] = {
83018315
_CURSES_SCR_INIT_METHODDEF
83028316
_CURSES_SCR_RESTORE_METHODDEF
83038317
_CURSES_SCR_SET_METHODDEF
8304-
#if defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20081102
83058318
_CURSES_GET_ESCDELAY_METHODDEF
83068319
_CURSES_SET_ESCDELAY_METHODDEF
8307-
#endif
83088320
_CURSES_GET_TABSIZE_METHODDEF
83098321
_CURSES_SET_TABSIZE_METHODDEF
83108322
_CURSES_SET_TERM_METHODDEF

Modules/clinic/_cursesmodule.c.h

Lines changed: 55 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)