gh-154749: Reject a terminal-less screen in curses.set_term() (GH-154750)
serhiy-storchaka <[email protected]> Mon, 27 Jul 2026 14:59:59 -0400 (EDT)
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/b099df5f177e43ebefde24a6714729277aa1bf8c commit: b099df5f177e43ebefde24a6714729277aa1bf8c branch: main author: Vyron Vasileiadis <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-07-27T18:59:46Z summary: gh-154749: Reject a terminal-less screen in curses.set_term() (GH-154750) set_term() accepted a screen returned by new_prescr(), which has no terminal, and the next refresh crashed inside curses. Raise curses.error instead. files: M Doc/library/curses.rst M Lib/test/test_curses.py M Modules/_cursesmodule.c diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 9d0bb239af06dfc..a833914a5d56d44 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -129,6 +129,8 @@ Initialization and termination and return the previously current screen. Returns ``None`` if the previous screen was the one created by :func:`initscr`. + Raises :exc:`error` if *screen* has no terminal, + as is the case for a screen returned by :func:`new_prescr`. .. versionadded:: next diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 31b7371abd32300..441eafd6e0d4776 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -3072,6 +3072,17 @@ def test_new_prescr(self): del screen gc_collect() + @requires_curses_func('new_prescr') + def test_set_term_prescr_screen(self): + # A new_prescr() screen has no terminal, so it cannot become the + # current one. It used to be accepted, and the next refresh then + # crashed inside curses. + s = self.make_pty() + screen = curses.newterm('xterm', s, s) + self.assertRaises(curses.error, curses.set_term, curses.new_prescr()) + # The current screen is unchanged, so refreshing it still works. + screen.stdscr.refresh() + def test_initscr_after_newterm_keeps_screen_alive(self): # initscr() called while a newterm() screen is current returns that # screen's own standard window, so the window keeps the screen alive. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2b580c3475e6d93..7314708cff7a814 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -6850,11 +6850,17 @@ _curses_set_term(PyObject *module, PyObject *screen) if (so == NULL) { return NULL; } + cursesmodule_state *state = get_cursesmodule_state(module); + if (so->stdscr_win == NULL) { + /* A screen from new_prescr() has no terminal, so it cannot become the + current one: a later refresh would dereference NULL in curses. */ + PyErr_SetString(state->error, "the screen has no terminal"); + return NULL; + } set_term(so->screen); if (!update_lines_cols(module)) { return NULL; } - cursesmodule_state *state = get_cursesmodule_state(module); PyObject *prev = state->topscreen; /* steal the owned reference */ state->topscreen = Py_NewRef(screen); return prev != NULL ? prev : Py_NewRef(Py_None); _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]