Skip to content

Commit 7e3dd1e

Browse files
gh-87904: Report the public module name in curses types and exceptions
The curses C types and exceptions now set their tp_name to the public module, so __module__, repr() and help() report curses.window, curses.complexchar, curses.complexstr, curses.screen, curses.error, curses.panel.panel and curses.panel.error instead of the underscore extension modules. This matches the convention used by decimal.Decimal, collections.deque and sqlite3.Error. The types are unchanged objects, still reachable through the _curses and _curses_panel modules. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent bbf7786 commit 7e3dd1e

4 files changed

Lines changed: 32 additions & 9 deletions

File tree

Lib/test/test_curses.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,8 @@ def test_complexchar(self):
442442
curses.complexchar('A', curses.A_BOLD))
443443
self.assertNotEqual(curses.complexchar('A'), curses.complexchar('B'))
444444
# repr() shows only a non-default attr/pair, and is a constructor call.
445-
ns = {'_curses': sys.modules[type(cc).__module__]}
445+
modname = type(cc).__module__
446+
ns = {modname: sys.modules[modname]}
446447
self.assertNotIn('attr=', repr(curses.complexchar('z')))
447448
self.assertNotIn('pair=', repr(curses.complexchar('z')))
448449
r = repr(curses.complexchar('A', curses.A_BOLD))
@@ -2187,6 +2188,24 @@ def test_has_extended_color_support(self):
21872188
r = curses.has_extended_color_support()
21882189
self.assertIsInstance(r, bool)
21892190

2191+
def test_type_names(self):
2192+
# The curses types report their public module rather than the
2193+
# underscore extension that implements them.
2194+
for name in 'window', 'complexchar', 'complexstr', 'screen', 'error':
2195+
tp = getattr(curses, name)
2196+
self.assertEqual(tp.__module__, 'curses')
2197+
self.assertEqual(tp.__qualname__, name)
2198+
self.assertEqual(tp.__name__, name)
2199+
2200+
@requires_curses_func('panel')
2201+
def test_panel_type_names(self):
2202+
import curses.panel
2203+
for name in 'panel', 'error':
2204+
tp = getattr(curses.panel, name)
2205+
self.assertEqual(tp.__module__, 'curses.panel')
2206+
self.assertEqual(tp.__qualname__, name)
2207+
self.assertEqual(tp.__name__, name)
2208+
21902209

21912210
class TestAscii(unittest.TestCase):
21922211

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :mod:`curses` types and exceptions now report their public module in
2+
:attr:`~type.__module__`, :func:`repr` and :func:`help` -- for example
3+
``curses.window`` instead of ``_curses.window`` and ``curses.panel.error``
4+
instead of ``_curses_panel.error``.

Modules/_curses_panel.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ static PyType_Slot PyCursesPanel_Type_slots[] = {
687687
};
688688

689689
static PyType_Spec PyCursesPanel_Type_spec = {
690-
.name = "_curses_panel.panel",
690+
.name = "curses.panel.panel",
691691
.basicsize = sizeof(PyCursesPanelObject),
692692
.flags = (
693693
Py_TPFLAGS_DEFAULT
@@ -826,9 +826,9 @@ _curses_panel_exec(PyObject *mod)
826826
return -1;
827827
}
828828

829-
/* For exception _curses_panel.error */
829+
/* For exception curses.panel.error */
830830
state->error = PyErr_NewExceptionWithDoc(
831-
"_curses_panel.error",
831+
"curses.panel.error",
832832
"Exception raised when a curses panel library function returns an error.",
833833
NULL, NULL);
834834

Modules/_cursesmodule.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4390,7 +4390,7 @@ static PyType_Slot PyCursesComplexChar_Type_slots[] = {
43904390
};
43914391

43924392
static PyType_Spec PyCursesComplexChar_Type_spec = {
4393-
.name = "_curses.complexchar",
4393+
.name = "curses.complexchar",
43944394
.basicsize = sizeof(PyCursesComplexCharObject),
43954395
.flags = Py_TPFLAGS_DEFAULT
43964396
| Py_TPFLAGS_IMMUTABLETYPE
@@ -4415,7 +4415,7 @@ static PyType_Slot PyCursesComplexStr_Type_slots[] = {
44154415
};
44164416

44174417
static PyType_Spec PyCursesComplexStr_Type_spec = {
4418-
.name = "_curses.complexstr",
4418+
.name = "curses.complexstr",
44194419
.basicsize = offsetof(PyCursesComplexStrObject, cells),
44204420
.itemsize = sizeof(cchar_t),
44214421
.flags = Py_TPFLAGS_DEFAULT
@@ -4778,7 +4778,7 @@ static PyType_Slot PyCursesWindow_Type_slots[] = {
47784778
};
47794779

47804780
static PyType_Spec PyCursesWindow_Type_spec = {
4781-
.name = "_curses.window",
4781+
.name = "curses.window",
47824782
.basicsize = sizeof(PyCursesWindowObject),
47834783
.flags = Py_TPFLAGS_DEFAULT
47844784
| Py_TPFLAGS_DISALLOW_INSTANTIATION
@@ -4954,7 +4954,7 @@ static PyType_Slot PyCursesScreen_Type_slots[] = {
49544954
};
49554955

49564956
static PyType_Spec PyCursesScreen_Type_spec = {
4957-
.name = "_curses.screen",
4957+
.name = "curses.screen",
49584958
.basicsize = sizeof(PyCursesScreenObject),
49594959
.flags = Py_TPFLAGS_DEFAULT
49604960
| Py_TPFLAGS_DISALLOW_INSTANTIATION
@@ -8042,7 +8042,7 @@ cursesmodule_exec(PyObject *module)
80428042

80438043
/* For exception curses.error */
80448044
state->error = PyErr_NewExceptionWithDoc(
8045-
"_curses.error",
8045+
"curses.error",
80468046
"Exception raised when a curses library function returns an error.",
80478047
NULL, NULL);
80488048
if (state->error == NULL) {

0 commit comments

Comments
 (0)