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]