From 80a55092f365aa40aadec0ebf0b3e1f85c6434c8 Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Mon, 27 Jul 2026 23:12:12 +0300 Subject: [PATCH] gh-154788: Make curses window.getparent() unconditional getparent() calls no curses function, it returns the window's stored parent, but it was compiled only when the ncurses extension functions were present. Close the guard after getscrreg(), which does need it. The test moves out of test_state_getters, which is gated on is_scrollok(), so that getparent() is covered on backends without the extensions. --- Lib/test/test_curses.py | 12 +++++++++--- Modules/_cursesmodule.c | 4 +--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 441eafd6e0d477..c589f58468beab 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -1718,6 +1718,14 @@ def test_input_options(self): stdscr.timeout(0) stdscr.timeout(5) + def test_getparent(self): + # getparent() calls no curses function, so it works with any backend + # and is not gated like the is_*() getters below. + stdscr = self.stdscr + self.assertIsNone(stdscr.getparent()) + sub = stdscr.subwin(3, 3, 0, 0) + self.assertIs(sub.getparent(), stdscr) + @requires_curses_window_meth('is_scrollok') def test_state_getters(self): stdscr = self.stdscr @@ -1771,13 +1779,11 @@ def test_state_getters(self): stdscr.setscrreg(5, 10) self.assertEqual(stdscr.getscrreg(), (5, 10)) - # is_pad()/is_subwin()/getparent(). + # is_pad()/is_subwin(). self.assertIs(stdscr.is_pad(), False) self.assertIs(stdscr.is_subwin(), False) - self.assertIsNone(stdscr.getparent()) sub = stdscr.subwin(3, 3, 0, 0) self.assertIs(sub.is_subwin(), True) - self.assertIs(sub.getparent(), stdscr) pad = curses.newpad(5, 5) self.assertIs(pad.is_pad(), True) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 7314708cff7a81..4fbb92074e7754 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1870,6 +1870,7 @@ PyCursesWindow_getscrreg(PyObject *op, PyObject *Py_UNUSED(ignored)) } return Py_BuildValue("(ii)", top, bottom); } +#endif /* NCURSES_EXT_FUNCS >= 20110404 || PDCURSES */ static PyObject * PyCursesWindow_getparent(PyObject *op, PyObject *Py_UNUSED(ignored)) @@ -1882,7 +1883,6 @@ PyCursesWindow_getparent(PyObject *op, PyObject *Py_UNUSED(ignored)) } return Py_NewRef((PyObject *)self->orig); } -#endif /* NCURSES_EXT_FUNCS >= 20110404 || PDCURSES */ Window_NoArgNoReturnVoidFunction(wsyncup) Window_NoArgNoReturnVoidFunction(wsyncdown) @@ -4963,11 +4963,9 @@ static PyMethodDef PyCursesWindow_methods[] = { {"getmaxyx", PyCursesWindow_getmaxyx, METH_NOARGS, "getmaxyx($self, /)\n--\n\n" "Return a tuple (y, x) of the window height and width."}, -#if (defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20110404) || defined(PDCURSES) {"getparent", PyCursesWindow_getparent, METH_NOARGS, "getparent($self, /)\n--\n\n" "Return the parent window, or None if this is not a subwindow."}, -#endif {"getparyx", PyCursesWindow_getparyx, METH_NOARGS, "getparyx($self, /)\n--\n\n" "Return (y, x) relative to the parent window, or (-1, -1) if none."},