diff --git a/Include/py_curses.h b/Include/py_curses.h index 79b1b01fcfa594..3e8b16c201f810 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 PyCursesWindow_Check(v) Py_IS_TYPE((v), &PyCursesWindow_Type) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 6fe0e7fd4b7fe9..778f040f7a6e77 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 @@ -187,6 +188,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 02b04645036d72..06943aefde6c86 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -666,7 +666,8 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") /* Allocation and deallocation of Window Objects */ static PyObject * -PyCursesWindow_New(WINDOW *win, const char *encoding) +PyCursesWindow_New(WINDOW *win, const char *encoding, + PyCursesWindowObject *orig) { PyCursesWindowObject *wo; @@ -697,6 +698,8 @@ PyCursesWindow_New(WINDOW *win, const char *encoding) PyErr_NoMemory(); return NULL; } + wo->orig = orig; + Py_XINCREF(orig); return (PyObject *)wo; } @@ -706,6 +709,7 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo) if (wo->win != stdscr) delwin(wo->win); if (wo->encoding != NULL) PyMem_Free(wo->encoding); + Py_XDECREF(wo->orig); PyObject_Free(wo); } @@ -1309,7 +1313,7 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1, return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(win, NULL, self); } /*[clinic input] @@ -2336,7 +2340,7 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, return NULL; } - return (PyObject *)PyCursesWindow_New(win, self->encoding); + return (PyObject *)PyCursesWindow_New(win, self->encoding, self); } /*[clinic input] @@ -3084,7 +3088,7 @@ _curses_getwin(PyObject *module, PyObject *file) PyErr_SetString(PyCursesError, catchall_NULL); goto error; } - res = PyCursesWindow_New(win, NULL); + res = PyCursesWindow_New(win, NULL, NULL); error: fclose(fp); @@ -3257,7 +3261,7 @@ _curses_initscr_impl(PyObject *module) if (initialised) { wrefresh(stdscr); - return (PyObject *)PyCursesWindow_New(stdscr, NULL); + return (PyObject *)PyCursesWindow_New(stdscr, NULL, NULL); } win = initscr(); @@ -3349,7 +3353,7 @@ _curses_initscr_impl(PyObject *module) SetDictInt("LINES", LINES); SetDictInt("COLS", COLS); - winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL, NULL); screen_encoding = winobj->encoding; return (PyObject *)winobj; } @@ -3728,7 +3732,7 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols) return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(win, NULL, NULL); } /*[clinic input] @@ -3767,7 +3771,7 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols, return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(win, NULL, NULL); } /*[clinic input]