@@ -32,6 +32,8 @@ typedef struct {
3232 PyTypeObject * PyCursesPanel_Type ;
3333} _curses_panel_state ;
3434
35+ typedef struct PyCursesPanelObject PyCursesPanelObject ;
36+
3537static inline _curses_panel_state *
3638get_curses_panel_state (PyObject * module )
3739{
@@ -40,6 +42,25 @@ get_curses_panel_state(PyObject *module)
4042 return (_curses_panel_state * )state ;
4143}
4244
45+ static inline _curses_panel_state *
46+ get_curses_panel_state_by_panel (PyCursesPanelObject * panel )
47+ {
48+ /*
49+ * Note: 'state' may be NULL if Py_TYPE(panel) is not a heap
50+ * type associated with this module, but the compiler would
51+ * have likely already complained with an "invalid pointer
52+ * type" at compile-time.
53+ *
54+ * To make it more robust, all functions recovering a module's
55+ * state from an object should expect to return NULL with an
56+ * exception set (in contrast to functions recovering a module's
57+ * state from a module itself).
58+ */
59+ void * state = PyType_GetModuleState (Py_TYPE (panel ));
60+ assert (state != NULL );
61+ return (_curses_panel_state * )state ;
62+ }
63+
4364static int
4465_curses_panel_clear (PyObject * mod )
4566{
@@ -95,7 +116,7 @@ PyCursesCheckERR(_curses_panel_state *state, int code, const char *fname)
95116
96117/* Definition of the panel object and panel type */
97118
98- typedef struct {
119+ typedef struct PyCursesPanelObject {
99120 PyObject_HEAD
100121 PANEL * pan ;
101122 PyCursesWindowObject * wo ; /* for reference counts */
@@ -262,8 +283,11 @@ static PyObject *
262283PyCursesPanel_New (_curses_panel_state * state , PANEL * pan ,
263284 PyCursesWindowObject * wo )
264285{
265- PyCursesPanelObject * po = PyObject_New (PyCursesPanelObject ,
266- state -> PyCursesPanel_Type );
286+ assert (state != NULL );
287+ PyTypeObject * type = state -> PyCursesPanel_Type ;
288+ assert (type != NULL );
289+ assert (type -> tp_alloc != NULL );
290+ PyCursesPanelObject * po = (PyCursesPanelObject * )type -> tp_alloc (type , 0 );
267291 if (po == NULL ) {
268292 return NULL ;
269293 }
@@ -278,27 +302,57 @@ PyCursesPanel_New(_curses_panel_state *state, PANEL *pan,
278302 return (PyObject * )po ;
279303}
280304
305+ static int
306+ PyCursesPanel_Clear (PyObject * op )
307+ {
308+ PyCursesPanelObject * self = _PyCursesPanelObject_CAST (op );
309+ PyObject * extra = (PyObject * )panel_userptr (self -> pan );
310+ if (extra != NULL ) {
311+ Py_DECREF (extra );
312+ if (set_panel_userptr (self -> pan , NULL ) == ERR ) {
313+ _curses_panel_state * state = get_curses_panel_state_by_panel (self );
314+ PyErr_SetString (state -> PyCursesError ,
315+ "set_panel_userptr() returned ERR" );
316+ return -1 ;
317+ }
318+ }
319+ // self->wo should not be cleared because an associated WINDOW may exist
320+ return 0 ;
321+ }
322+
281323static void
282324PyCursesPanel_Dealloc (PyObject * self )
283325{
284- PyObject * tp , * obj ;
285- PyCursesPanelObject * po = _PyCursesPanelObject_CAST (self );
326+ PyTypeObject * tp = Py_TYPE ( self ) ;
327+ PyObject_GC_UnTrack (self );
286328
287- tp = (PyObject * ) Py_TYPE (po );
288- obj = (PyObject * ) panel_userptr (po -> pan );
289- if (obj ) {
290- (void )set_panel_userptr (po -> pan , NULL );
291- Py_DECREF (obj );
329+ PyCursesPanelObject * po = _PyCursesPanelObject_CAST (self );
330+ if (PyCursesPanel_Clear (self ) < 0 ) {
331+ PyErr_FormatUnraisable ("Exception ignored in PyCursesPanel_Dealloc()" );
332+ }
333+ if (del_panel (po -> pan ) == ERR && !PyErr_Occurred ()) {
334+ _curses_panel_state * state = get_curses_panel_state_by_panel (po );
335+ PyErr_SetString (state -> PyCursesError , "del_panel() returned ERR" );
336+ PyErr_FormatUnraisable ("Exception ignored in PyCursesPanel_Dealloc()" );
292337 }
293- (void )del_panel (po -> pan );
294338 if (po -> wo != NULL ) {
295339 Py_DECREF (po -> wo );
296340 remove_lop (po );
297341 }
298- PyObject_Free (po );
342+ tp -> tp_free (po );
299343 Py_DECREF (tp );
300344}
301345
346+ static int
347+ PyCursesPanel_Traverse (PyObject * op , visitproc visit , void * arg )
348+ {
349+ PyCursesPanelObject * self = _PyCursesPanelObject_CAST (op );
350+ Py_VISIT (Py_TYPE (op ));
351+ Py_VISIT (panel_userptr (self -> pan ));
352+ Py_VISIT (self -> wo );
353+ return 0 ;
354+ }
355+
302356/* panel_above(NULL) returns the bottom panel in the stack. To get
303357 this behaviour we use curses.panel.bottom_panel(). */
304358/*[clinic input]
@@ -520,15 +574,21 @@ static PyMethodDef PyCursesPanel_Methods[] = {
520574/* -------------------------------------------------------*/
521575
522576static PyType_Slot PyCursesPanel_Type_slots [] = {
577+ {Py_tp_clear , PyCursesPanel_Clear },
523578 {Py_tp_dealloc , PyCursesPanel_Dealloc },
579+ {Py_tp_traverse , PyCursesPanel_Traverse },
524580 {Py_tp_methods , PyCursesPanel_Methods },
525581 {0 , 0 },
526582};
527583
528584static PyType_Spec PyCursesPanel_Type_spec = {
529585 .name = "_curses_panel.panel" ,
530586 .basicsize = sizeof (PyCursesPanelObject ),
531- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION ,
587+ .flags = (
588+ Py_TPFLAGS_DEFAULT
589+ | Py_TPFLAGS_DISALLOW_INSTANTIATION
590+ | Py_TPFLAGS_HAVE_GC
591+ ),
532592 .slots = PyCursesPanel_Type_slots
533593};
534594
0 commit comments