Skip to content

Commit f66d785

Browse files
authored
gh-123961: Convert curses.window static type into a heap type (#124934)
1 parent 5e9e506 commit f66d785

File tree

3 files changed

+120
-74
lines changed

3 files changed

+120
-74
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Convert the :ref:`curses.window <curses-window-objects>` static type exposed
2+
by the :c:macro:`!PyCursesWindow_Type` macro in ``Include/py_curses.h`` to a
3+
:ref:`heap type <heap-types>`. Patch by Bénédikt Tran.

Modules/_cursesmodule.c

Lines changed: 112 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ static const char PyCursesVersion[] = "2.2";
105105
#endif
106106

107107
#include "Python.h"
108-
#include "pycore_long.h" // _PyLong_GetZero()
109-
#include "pycore_structseq.h" // _PyStructSequence_NewType()
108+
#include "pycore_capsule.h" // _PyCapsule_SetTraverse()
109+
#include "pycore_long.h" // _PyLong_GetZero()
110+
#include "pycore_structseq.h" // _PyStructSequence_NewType()
110111

111112
#ifdef __hpux
112113
#define STRICT_SYSV_CURSES
@@ -173,6 +174,12 @@ get_cursesmodule_state(PyObject *Py_UNUSED(module))
173174
return &curses_global_state;
174175
}
175176

177+
static inline _cursesmodule_state *
178+
get_cursesmodule_state_by_cls(PyTypeObject *Py_UNUSED(cls))
179+
{
180+
return &curses_global_state;
181+
}
182+
176183
static inline _cursesmodule_state *
177184
get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))
178185
{
@@ -181,9 +188,9 @@ get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))
181188

182189
/*[clinic input]
183190
module _curses
184-
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
191+
class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type"
185192
[clinic start generated code]*/
186-
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=43265c372c2887d6]*/
193+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/
187194

188195
/* Tells whether setupterm() has been called to initialise terminfo. */
189196
static int curses_setupterm_called = FALSE;
@@ -630,10 +637,6 @@ class component_converter(CConverter):
630637
The Window Object
631638
******************************************************************************/
632639

633-
/* Definition of the window type */
634-
635-
PyTypeObject PyCursesWindow_Type;
636-
637640
/* Function prototype macros for Window object
638641
639642
X - function name
@@ -743,10 +746,9 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns")
743746
/* Allocation and deallocation of Window Objects */
744747

745748
static PyObject *
746-
PyCursesWindow_New(WINDOW *win, const char *encoding)
749+
PyCursesWindow_New(_cursesmodule_state *state,
750+
WINDOW *win, const char *encoding)
747751
{
748-
PyCursesWindowObject *wo;
749-
750752
if (encoding == NULL) {
751753
#if defined(MS_WINDOWS)
752754
char *buffer[100];
@@ -758,36 +760,53 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
758760
}
759761
#elif defined(CODESET)
760762
const char *codeset = nl_langinfo(CODESET);
761-
if (codeset != NULL && codeset[0] != 0)
763+
if (codeset != NULL && codeset[0] != 0) {
762764
encoding = codeset;
765+
}
763766
#endif
764-
if (encoding == NULL)
767+
if (encoding == NULL) {
765768
encoding = "utf-8";
769+
}
766770
}
767771

768-
wo = PyObject_New(PyCursesWindowObject, &PyCursesWindow_Type);
769-
if (wo == NULL) return NULL;
772+
PyCursesWindowObject *wo = PyObject_GC_New(PyCursesWindowObject,
773+
state->window_type);
774+
if (wo == NULL) {
775+
return NULL;
776+
}
770777
wo->win = win;
771778
wo->encoding = _PyMem_Strdup(encoding);
772779
if (wo->encoding == NULL) {
773780
Py_DECREF(wo);
774781
PyErr_NoMemory();
775782
return NULL;
776783
}
784+
PyObject_GC_Track((PyObject *)wo);
777785
return (PyObject *)wo;
778786
}
779787

780788
static void
781-
PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
789+
PyCursesWindow_dealloc(PyObject *self)
782790
{
791+
PyTypeObject *window_type = Py_TYPE(self);
792+
PyObject_GC_UnTrack(self);
793+
PyCursesWindowObject *wo = (PyCursesWindowObject *)self;
783794
if (wo->win != stdscr && wo->win != NULL) {
784795
// silently ignore errors in delwin(3)
785796
(void)delwin(wo->win);
786797
}
787798
if (wo->encoding != NULL) {
788799
PyMem_Free(wo->encoding);
789800
}
790-
PyObject_Free(wo);
801+
window_type->tp_free(self);
802+
Py_DECREF(window_type);
803+
}
804+
805+
static int
806+
PyCursesWindow_traverse(PyObject *self, visitproc visit, void *arg)
807+
{
808+
Py_VISIT(Py_TYPE(self));
809+
return 0;
791810
}
792811

793812
/* Addch, Addstr, Addnstr */
@@ -1391,7 +1410,8 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1,
13911410
return NULL;
13921411
}
13931412

1394-
return (PyObject *)PyCursesWindow_New(win, NULL);
1413+
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
1414+
return PyCursesWindow_New(state, win, NULL);
13951415
}
13961416

13971417
/*[clinic input]
@@ -2140,7 +2160,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self)
21402160
/*[clinic input]
21412161
_curses.window.overlay
21422162
2143-
destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
2163+
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")
21442164
21452165
[
21462166
sminrow: int
@@ -2169,7 +2189,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
21692189
PyCursesWindowObject *destwin, int group_right_1,
21702190
int sminrow, int smincol, int dminrow,
21712191
int dmincol, int dmaxrow, int dmaxcol)
2172-
/*[clinic end generated code: output=82bb2c4cb443ca58 input=7edd23ad22cc1984]*/
2192+
/*[clinic end generated code: output=82bb2c4cb443ca58 input=6e4b32a7c627a356]*/
21732193
{
21742194
int rtn;
21752195

@@ -2187,7 +2207,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
21872207
/*[clinic input]
21882208
_curses.window.overwrite
21892209
2190-
destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
2210+
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")
21912211
21922212
[
21932213
sminrow: int
@@ -2217,7 +2237,7 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self,
22172237
int group_right_1, int sminrow, int smincol,
22182238
int dminrow, int dmincol, int dmaxrow,
22192239
int dmaxcol)
2220-
/*[clinic end generated code: output=12ae007d1681be28 input=ea5de1b35cd948e0]*/
2240+
/*[clinic end generated code: output=12ae007d1681be28 input=d83dd8b24ff2bcc9]*/
22212241
{
22222242
int rtn;
22232243

@@ -2426,7 +2446,8 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1,
24262446
return NULL;
24272447
}
24282448

2429-
return (PyObject *)PyCursesWindow_New(win, self->encoding);
2449+
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
2450+
return PyCursesWindow_New(state, win, self->encoding);
24302451
}
24312452

24322453
/*[clinic input]
@@ -2564,9 +2585,11 @@ PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *P
25642585
return 0;
25652586
}
25662587

2588+
#define clinic_state() (get_cursesmodule_state_by_cls(Py_TYPE(self)))
25672589
#include "clinic/_cursesmodule.c.h"
2590+
#undef clinic_state
25682591

2569-
static PyMethodDef PyCursesWindow_Methods[] = {
2592+
static PyMethodDef PyCursesWindow_methods[] = {
25702593
_CURSES_WINDOW_ADDCH_METHODDEF
25712594
_CURSES_WINDOW_ADDNSTR_METHODDEF
25722595
_CURSES_WINDOW_ADDSTR_METHODDEF
@@ -2660,42 +2683,27 @@ static PyGetSetDef PyCursesWindow_getsets[] = {
26602683
{NULL, NULL, NULL, NULL } /* sentinel */
26612684
};
26622685

2663-
/* -------------------------------------------------------*/
2686+
static PyType_Slot PyCursesWindow_Type_slots[] = {
2687+
{Py_tp_methods, PyCursesWindow_methods},
2688+
{Py_tp_getset, PyCursesWindow_getsets},
2689+
{Py_tp_dealloc, PyCursesWindow_dealloc},
2690+
{Py_tp_traverse, PyCursesWindow_traverse},
2691+
{0, NULL}
2692+
};
26642693

2665-
PyTypeObject PyCursesWindow_Type = {
2666-
PyVarObject_HEAD_INIT(NULL, 0)
2667-
"_curses.window", /*tp_name*/
2668-
sizeof(PyCursesWindowObject), /*tp_basicsize*/
2669-
0, /*tp_itemsize*/
2670-
/* methods */
2671-
(destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/
2672-
0, /*tp_vectorcall_offset*/
2673-
(getattrfunc)0, /*tp_getattr*/
2674-
(setattrfunc)0, /*tp_setattr*/
2675-
0, /*tp_as_async*/
2676-
0, /*tp_repr*/
2677-
0, /*tp_as_number*/
2678-
0, /*tp_as_sequence*/
2679-
0, /*tp_as_mapping*/
2680-
0, /*tp_hash*/
2681-
0, /*tp_call*/
2682-
0, /*tp_str*/
2683-
0, /*tp_getattro*/
2684-
0, /*tp_setattro*/
2685-
0, /*tp_as_buffer*/
2686-
Py_TPFLAGS_DEFAULT, /*tp_flags*/
2687-
0, /*tp_doc*/
2688-
0, /*tp_traverse*/
2689-
0, /*tp_clear*/
2690-
0, /*tp_richcompare*/
2691-
0, /*tp_weaklistoffset*/
2692-
0, /*tp_iter*/
2693-
0, /*tp_iternext*/
2694-
PyCursesWindow_Methods, /*tp_methods*/
2695-
0, /* tp_members */
2696-
PyCursesWindow_getsets, /* tp_getset */
2694+
static PyType_Spec PyCursesWindow_Type_spec = {
2695+
.name = "_curses.window",
2696+
.basicsize = sizeof(PyCursesWindowObject),
2697+
.flags = Py_TPFLAGS_DEFAULT
2698+
| Py_TPFLAGS_DISALLOW_INSTANTIATION
2699+
| Py_TPFLAGS_IMMUTABLETYPE
2700+
| Py_TPFLAGS_HEAPTYPE
2701+
| Py_TPFLAGS_HAVE_GC,
2702+
.slots = PyCursesWindow_Type_slots
26972703
};
26982704

2705+
/* -------------------------------------------------------*/
2706+
26992707
/* Function Body Macros - They are ugly but very, very useful. ;-)
27002708
27012709
X - function name
@@ -3177,7 +3185,8 @@ _curses_getwin(PyObject *module, PyObject *file)
31773185
PyErr_SetString(state->error, catchall_NULL);
31783186
goto error;
31793187
}
3180-
res = PyCursesWindow_New(win, NULL);
3188+
_cursesmodule_state *state = get_cursesmodule_state(module);
3189+
res = PyCursesWindow_New(state, win, NULL);
31813190

31823191
error:
31833192
fclose(fp);
@@ -3349,7 +3358,8 @@ _curses_initscr_impl(PyObject *module)
33493358

33503359
if (curses_initscr_called) {
33513360
wrefresh(stdscr);
3352-
return (PyObject *)PyCursesWindow_New(stdscr, NULL);
3361+
_cursesmodule_state *state = get_cursesmodule_state(module);
3362+
return PyCursesWindow_New(state, stdscr, NULL);
33533363
}
33543364

33553365
win = initscr();
@@ -3452,12 +3462,13 @@ _curses_initscr_impl(PyObject *module)
34523462
SetDictInt("COLS", COLS);
34533463
#undef SetDictInt
34543464

3455-
PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
3465+
_cursesmodule_state *state = get_cursesmodule_state(module);
3466+
PyObject *winobj = PyCursesWindow_New(state, win, NULL);
34563467
if (winobj == NULL) {
34573468
return NULL;
34583469
}
3459-
curses_screen_encoding = winobj->encoding;
3460-
return (PyObject *)winobj;
3470+
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
3471+
return winobj;
34613472
}
34623473

34633474
/*[clinic input]
@@ -3829,7 +3840,8 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols)
38293840
return NULL;
38303841
}
38313842

3832-
return (PyObject *)PyCursesWindow_New(win, NULL);
3843+
_cursesmodule_state *state = get_cursesmodule_state(module);
3844+
return PyCursesWindow_New(state, win, NULL);
38333845
}
38343846

38353847
/*[clinic input]
@@ -3869,7 +3881,8 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols,
38693881
return NULL;
38703882
}
38713883

3872-
return (PyObject *)PyCursesWindow_New(win, NULL);
3884+
_cursesmodule_state *state = get_cursesmodule_state(module);
3885+
return PyCursesWindow_New(state, win, NULL);
38733886
}
38743887

38753888
/*[clinic input]
@@ -4893,11 +4906,40 @@ curses_capi_capsule_destructor(PyObject *op)
48934906
curses_capi_free(capi);
48944907
}
48954908

4909+
static int
4910+
curses_capi_capsule_traverse(PyObject *op, visitproc visit, void *arg)
4911+
{
4912+
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
4913+
assert(capi_ptr != NULL);
4914+
Py_VISIT(capi_ptr[0]); // visit curses window type
4915+
return 0;
4916+
}
4917+
4918+
static int
4919+
curses_capi_capsule_clear(PyObject *op)
4920+
{
4921+
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
4922+
assert(capi_ptr != NULL);
4923+
Py_CLEAR(capi_ptr[0]); // clear curses window type
4924+
return 0;
4925+
}
4926+
48964927
static PyObject *
48974928
curses_capi_capsule_new(void *capi)
48984929
{
4899-
return PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
4900-
curses_capi_capsule_destructor);
4930+
PyObject *capsule = PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
4931+
curses_capi_capsule_destructor);
4932+
if (capsule == NULL) {
4933+
return NULL;
4934+
}
4935+
if (_PyCapsule_SetTraverse(capsule,
4936+
curses_capi_capsule_traverse,
4937+
curses_capi_capsule_clear) < 0)
4938+
{
4939+
Py_DECREF(capsule);
4940+
return NULL;
4941+
}
4942+
return capsule;
49014943
}
49024944

49034945
/* Module initialization */
@@ -4907,13 +4949,14 @@ cursesmodule_exec(PyObject *module)
49074949
{
49084950
_cursesmodule_state *state = get_cursesmodule_state(module);
49094951
/* Initialize object type */
4910-
if (PyType_Ready(&PyCursesWindow_Type) < 0) {
4952+
state->window_type = (PyTypeObject *)PyType_FromModuleAndSpec(
4953+
module, &PyCursesWindow_Type_spec, NULL);
4954+
if (state->window_type == NULL) {
49114955
return -1;
49124956
}
4913-
if (PyModule_AddType(module, &PyCursesWindow_Type) < 0) {
4957+
if (PyModule_AddType(module, state->window_type) < 0) {
49144958
return -1;
49154959
}
4916-
state->window_type = &PyCursesWindow_Type;
49174960

49184961
/* Add some symbolic constants to the module */
49194962
PyObject *module_dict = PyModule_GetDict(module);

0 commit comments

Comments
 (0)