Skip to content

gh-123961: convert curses.window static type into a heap type #124934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Convert the :ref:`curses.window <curses-window-objects>` static type exposed
by the :c:macro:`!PyCursesWindow_Type` macro in ``Include/py_curses.h`` to a
:ref:`heap type <heap-types>`. Patch by Bénédikt Tran.
181 changes: 112 additions & 69 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ static const char PyCursesVersion[] = "2.2";
#endif

#include "Python.h"
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_structseq.h" // _PyStructSequence_NewType()
#include "pycore_capsule.h" // _PyCapsule_SetTraverse()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_structseq.h" // _PyStructSequence_NewType()

#ifdef __hpux
#define STRICT_SYSV_CURSES
Expand Down Expand Up @@ -173,6 +174,12 @@ get_cursesmodule_state(PyObject *Py_UNUSED(module))
return &curses_global_state;
}

static inline _cursesmodule_state *
get_cursesmodule_state_by_cls(PyTypeObject *Py_UNUSED(cls))
{
return &curses_global_state;
}

static inline _cursesmodule_state *
get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))
{
Expand All @@ -181,9 +188,9 @@ get_cursesmodule_state_by_win(PyCursesWindowObject *Py_UNUSED(win))

/*[clinic input]
module _curses
class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type"
class _curses.window "PyCursesWindowObject *" "clinic_state()->window_type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=43265c372c2887d6]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae6cb623018f2cbc]*/

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

/* Definition of the window type */

PyTypeObject PyCursesWindow_Type;

/* Function prototype macros for Window object

X - function name
Expand Down Expand Up @@ -743,10 +746,9 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns")
/* Allocation and deallocation of Window Objects */

static PyObject *
PyCursesWindow_New(WINDOW *win, const char *encoding)
PyCursesWindow_New(_cursesmodule_state *state,
WINDOW *win, const char *encoding)
{
PyCursesWindowObject *wo;

if (encoding == NULL) {
#if defined(MS_WINDOWS)
char *buffer[100];
Expand All @@ -758,36 +760,53 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
}
#elif defined(CODESET)
const char *codeset = nl_langinfo(CODESET);
if (codeset != NULL && codeset[0] != 0)
if (codeset != NULL && codeset[0] != 0) {
encoding = codeset;
}
#endif
if (encoding == NULL)
if (encoding == NULL) {
encoding = "utf-8";
}
}

wo = PyObject_New(PyCursesWindowObject, &PyCursesWindow_Type);
if (wo == NULL) return NULL;
PyCursesWindowObject *wo = PyObject_GC_New(PyCursesWindowObject,
state->window_type);
if (wo == NULL) {
return NULL;
}
wo->win = win;
wo->encoding = _PyMem_Strdup(encoding);
if (wo->encoding == NULL) {
Py_DECREF(wo);
PyErr_NoMemory();
return NULL;
}
PyObject_GC_Track((PyObject *)wo);
return (PyObject *)wo;
}

static void
PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
PyCursesWindow_dealloc(PyObject *self)
{
PyTypeObject *window_type = Py_TYPE(self);
PyObject_GC_UnTrack(self);
PyCursesWindowObject *wo = (PyCursesWindowObject *)self;
if (wo->win != stdscr && wo->win != NULL) {
// silently ignore errors in delwin(3)
(void)delwin(wo->win);
}
if (wo->encoding != NULL) {
PyMem_Free(wo->encoding);
}
PyObject_Free(wo);
window_type->tp_free(self);
Py_DECREF(window_type);
}

static int
PyCursesWindow_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
return 0;
}

/* Addch, Addstr, Addnstr */
Expand Down Expand Up @@ -1391,7 +1410,8 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -2140,7 +2160,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self)
/*[clinic input]
_curses.window.overlay

destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")

[
sminrow: int
Expand Down Expand Up @@ -2169,7 +2189,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
PyCursesWindowObject *destwin, int group_right_1,
int sminrow, int smincol, int dminrow,
int dmincol, int dmaxrow, int dmaxcol)
/*[clinic end generated code: output=82bb2c4cb443ca58 input=7edd23ad22cc1984]*/
/*[clinic end generated code: output=82bb2c4cb443ca58 input=6e4b32a7c627a356]*/
{
int rtn;

Expand All @@ -2187,7 +2207,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self,
/*[clinic input]
_curses.window.overwrite

destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type")
destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->window_type")

[
sminrow: int
Expand Down Expand Up @@ -2217,7 +2237,7 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self,
int group_right_1, int sminrow, int smincol,
int dminrow, int dmincol, int dmaxrow,
int dmaxcol)
/*[clinic end generated code: output=12ae007d1681be28 input=ea5de1b35cd948e0]*/
/*[clinic end generated code: output=12ae007d1681be28 input=d83dd8b24ff2bcc9]*/
{
int rtn;

Expand Down Expand Up @@ -2426,7 +2446,8 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, self->encoding);
_cursesmodule_state *state = get_cursesmodule_state_by_win(self);
return PyCursesWindow_New(state, win, self->encoding);
}

/*[clinic input]
Expand Down Expand Up @@ -2564,9 +2585,11 @@ PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *P
return 0;
}

#define clinic_state() (get_cursesmodule_state_by_cls(Py_TYPE(self)))
#include "clinic/_cursesmodule.c.h"
#undef clinic_state

static PyMethodDef PyCursesWindow_Methods[] = {
static PyMethodDef PyCursesWindow_methods[] = {
_CURSES_WINDOW_ADDCH_METHODDEF
_CURSES_WINDOW_ADDNSTR_METHODDEF
_CURSES_WINDOW_ADDSTR_METHODDEF
Expand Down Expand Up @@ -2660,42 +2683,27 @@ static PyGetSetDef PyCursesWindow_getsets[] = {
{NULL, NULL, NULL, NULL } /* sentinel */
};

/* -------------------------------------------------------*/
static PyType_Slot PyCursesWindow_Type_slots[] = {
{Py_tp_methods, PyCursesWindow_methods},
{Py_tp_getset, PyCursesWindow_getsets},
{Py_tp_dealloc, PyCursesWindow_dealloc},
{Py_tp_traverse, PyCursesWindow_traverse},
{0, NULL}
};

PyTypeObject PyCursesWindow_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_curses.window", /*tp_name*/
sizeof(PyCursesWindowObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/
0, /*tp_vectorcall_offset*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
0, /*tp_as_async*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
PyCursesWindow_Methods, /*tp_methods*/
0, /* tp_members */
PyCursesWindow_getsets, /* tp_getset */
static PyType_Spec PyCursesWindow_Type_spec = {
.name = "_curses.window",
.basicsize = sizeof(PyCursesWindowObject),
.flags = Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_IMMUTABLETYPE
| Py_TPFLAGS_HEAPTYPE
| Py_TPFLAGS_HAVE_GC,
.slots = PyCursesWindow_Type_slots
};

/* -------------------------------------------------------*/

/* Function Body Macros - They are ugly but very, very useful. ;-)

X - function name
Expand Down Expand Up @@ -3177,7 +3185,8 @@ _curses_getwin(PyObject *module, PyObject *file)
PyErr_SetString(state->error, catchall_NULL);
goto error;
}
res = PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
res = PyCursesWindow_New(state, win, NULL);

error:
fclose(fp);
Expand Down Expand Up @@ -3349,7 +3358,8 @@ _curses_initscr_impl(PyObject *module)

if (curses_initscr_called) {
wrefresh(stdscr);
return (PyObject *)PyCursesWindow_New(stdscr, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, stdscr, NULL);
}

win = initscr();
Expand Down Expand Up @@ -3452,12 +3462,13 @@ _curses_initscr_impl(PyObject *module)
SetDictInt("COLS", COLS);
#undef SetDictInt

PyCursesWindowObject *winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
PyObject *winobj = PyCursesWindow_New(state, win, NULL);
if (winobj == NULL) {
return NULL;
}
curses_screen_encoding = winobj->encoding;
return (PyObject *)winobj;
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
return winobj;
}

/*[clinic input]
Expand Down Expand Up @@ -3829,7 +3840,8 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols)
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -3869,7 +3881,8 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols,
return NULL;
}

return (PyObject *)PyCursesWindow_New(win, NULL);
_cursesmodule_state *state = get_cursesmodule_state(module);
return PyCursesWindow_New(state, win, NULL);
}

/*[clinic input]
Expand Down Expand Up @@ -4893,11 +4906,40 @@ curses_capi_capsule_destructor(PyObject *op)
curses_capi_free(capi);
}

static int
curses_capi_capsule_traverse(PyObject *op, visitproc visit, void *arg)
{
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
assert(capi_ptr != NULL);
Py_VISIT(capi_ptr[0]); // visit curses window type
return 0;
}

static int
curses_capi_capsule_clear(PyObject *op)
{
void **capi_ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME);
assert(capi_ptr != NULL);
Py_CLEAR(capi_ptr[0]); // clear curses window type
return 0;
}

static PyObject *
curses_capi_capsule_new(void *capi)
{
return PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
curses_capi_capsule_destructor);
PyObject *capsule = PyCapsule_New(capi, PyCurses_CAPSULE_NAME,
curses_capi_capsule_destructor);
if (capsule == NULL) {
return NULL;
}
if (_PyCapsule_SetTraverse(capsule,
curses_capi_capsule_traverse,
curses_capi_capsule_clear) < 0)
{
Py_DECREF(capsule);
return NULL;
}
return capsule;
}

/* Module initialization */
Expand All @@ -4907,13 +4949,14 @@ cursesmodule_exec(PyObject *module)
{
_cursesmodule_state *state = get_cursesmodule_state(module);
/* Initialize object type */
if (PyType_Ready(&PyCursesWindow_Type) < 0) {
state->window_type = (PyTypeObject *)PyType_FromModuleAndSpec(
module, &PyCursesWindow_Type_spec, NULL);
if (state->window_type == NULL) {
return -1;
}
if (PyModule_AddType(module, &PyCursesWindow_Type) < 0) {
if (PyModule_AddType(module, state->window_type) < 0) {
return -1;
}
state->window_type = &PyCursesWindow_Type;

/* Add some symbolic constants to the module */
PyObject *module_dict = PyModule_GetDict(module);
Expand Down
Loading
Loading