diff --git a/Include/py_curses.h b/Include/py_curses.h index e11bfedb17d205..49fc3c9d127aa6 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -75,10 +75,11 @@ extern "C" { /* Type declarations */ -typedef struct { +typedef struct PyCursesWindowObject { PyObject_HEAD WINDOW *win; char *encoding; + struct PyCursesWindowObject *orig; } PyCursesWindowObject; #define PyCurses_CAPSULE_NAME "_curses._C_API" diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index cc3aa561cd4c42..116112f9feb6bc 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -8,7 +8,8 @@ from unittest.mock import MagicMock from test.support import (requires, verbose, SaveSignals, cpython_only, - check_disallow_instantiation, MISSING_C_DOCSTRINGS) + check_disallow_instantiation, MISSING_C_DOCSTRINGS, + gc_collect) from test.support.import_helper import import_module # Optionally test curses module. This currently requires that the @@ -181,6 +182,14 @@ def test_create_windows(self): self.assertEqual(win3.getparyx(), (2, 1)) self.assertEqual(win3.getmaxyx(), (6, 11)) + def test_subwindows_references(self): + win = curses.newwin(5, 10) + win2 = win.subwin(3, 7) + del win + gc_collect() + del win2 + gc_collect() + def test_move_cursor(self): stdscr = self.stdscr win = stdscr.subwin(10, 15, 2, 5) diff --git a/Misc/NEWS.d/next/Library/2021-05-18-19-12-58.bpo-44172.rJ_-CI.rst b/Misc/NEWS.d/next/Library/2021-05-18-19-12-58.bpo-44172.rJ_-CI.rst new file mode 100644 index 00000000000000..d53f3725100eb2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-05-18-19-12-58.bpo-44172.rJ_-CI.rst @@ -0,0 +1,2 @@ +Keep a reference to original :mod:`curses` windows in subwindows so +that the original window does not get deleted before subwindows. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index bf18cb51605075..6da3ab0fe251f1 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -787,7 +787,8 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") static PyObject * PyCursesWindow_New(cursesmodule_state *state, - WINDOW *win, const char *encoding) + WINDOW *win, const char *encoding, + PyCursesWindowObject *orig) { if (encoding == NULL) { #if defined(MS_WINDOWS) @@ -821,6 +822,8 @@ PyCursesWindow_New(cursesmodule_state *state, PyErr_NoMemory(); return NULL; } + wo->orig = orig; + Py_XINCREF(orig); PyObject_GC_Track((PyObject *)wo); return (PyObject *)wo; } @@ -838,6 +841,7 @@ PyCursesWindow_dealloc(PyObject *self) if (wo->encoding != NULL) { PyMem_Free(wo->encoding); } + Py_XDECREF(wo->orig); window_type->tp_free(self); Py_DECREF(window_type); } @@ -846,6 +850,8 @@ static int PyCursesWindow_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); + PyCursesWindowObject *wo = (PyCursesWindowObject *)self; + Py_VISIT(wo->orig); return 0; } @@ -1453,7 +1459,7 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1, } cursesmodule_state *state = get_cursesmodule_state_by_win(self); - return PyCursesWindow_New(state, win, NULL); + return PyCursesWindow_New(state, win, NULL, self); } /*[clinic input] @@ -2493,7 +2499,7 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, } cursesmodule_state *state = get_cursesmodule_state_by_win(self); - return PyCursesWindow_New(state, win, self->encoding); + return PyCursesWindow_New(state, win, self->encoding, self); } /*[clinic input] @@ -3237,7 +3243,7 @@ _curses_getwin(PyObject *module, PyObject *file) goto error; } cursesmodule_state *state = get_cursesmodule_state(module); - res = PyCursesWindow_New(state, win, NULL); + res = PyCursesWindow_New(state, win, NULL, NULL); error: fclose(fp); @@ -3410,7 +3416,7 @@ _curses_initscr_impl(PyObject *module) if (curses_initscr_called) { wrefresh(stdscr); cursesmodule_state *state = get_cursesmodule_state(module); - return PyCursesWindow_New(state, stdscr, NULL); + return PyCursesWindow_New(state, stdscr, NULL, NULL); } win = initscr(); @@ -3514,7 +3520,7 @@ _curses_initscr_impl(PyObject *module) #undef SetDictInt cursesmodule_state *state = get_cursesmodule_state(module); - PyObject *winobj = PyCursesWindow_New(state, win, NULL); + PyObject *winobj = PyCursesWindow_New(state, win, NULL, NULL); if (winobj == NULL) { return NULL; } @@ -3898,7 +3904,7 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols) } cursesmodule_state *state = get_cursesmodule_state(module); - return PyCursesWindow_New(state, win, NULL); + return PyCursesWindow_New(state, win, NULL, NULL); } /*[clinic input] @@ -3939,7 +3945,7 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols, } cursesmodule_state *state = get_cursesmodule_state(module); - return PyCursesWindow_New(state, win, NULL); + return PyCursesWindow_New(state, win, NULL, NULL); } /*[clinic input]