From e113985ed3f89853d111c8790c2310a4f8e82939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:12:39 +0200 Subject: [PATCH 01/23] do not suppress errors in `PyCursesWindow_Dealloc` --- Modules/_cursesmodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2e6ec822e2d5b6..dc7d8421c47a52 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -932,8 +932,9 @@ PyCursesWindow_dealloc(PyObject *self) PyObject_GC_UnTrack(self); PyCursesWindowObject *wo = (PyCursesWindowObject *)self; if (wo->win != stdscr && wo->win != NULL) { - // silently ignore errors in delwin(3) - (void)delwin(wo->win); + if (delwin(wo->win) == ERR) { + PyErr_FormatUnraisable("delwin() error ignored"); + } } if (wo->encoding != NULL) { PyMem_Free(wo->encoding); From 953b4e875ad5af0fd112ea48ebd2b666725a8ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:13:52 +0200 Subject: [PATCH 02/23] do not suppress errors in `setcchar` --- Modules/_cursesmodule.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index dc7d8421c47a52..fcc96dbc424539 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1002,7 +1002,11 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, type = PyCurses_ConvertToCchar_t(self, ch, &cch, wstr); if (type == 2) { wstr[1] = L'\0'; - setcchar(&wcval, wstr, attr, PAIR_NUMBER(attr), NULL); + rtn = setcchar(&wcval, wstr, attr, PAIR_NUMBER(attr), NULL); + if (rtn == ERR) { + curses_window_set_error(self, "setcchar", "addch"); + return NULL; + } if (coordinates_group) { rtn = mvwadd_wch(self->win,y,x, &wcval); funcname = "mvwadd_wch"; From a474f30e653aa732783e8559ff5c78857176fecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:15:04 +0200 Subject: [PATCH 03/23] do not suppress errors in `wattrset` --- Modules/_cursesmodule.c | 89 +++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index fcc96dbc424539..7a0def9c6e92ac 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1036,6 +1036,27 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, return curses_window_check_err(self, rtn, funcname, "addch"); } +#ifdef HAVE_NCURSESW +#define curses_release_wstr(STRTYPE, WSTR) \ + do { \ + if ((STRTYPE) == 2) { \ + PyMem_Free((WSTR)); \ + } \ + } while (0) +#else +#define curses_release_wstr(_STRTYPE, _WSTR) +#endif + +static int +curses_wattrset(PyCursesWindowObject *self, long attr, const char *funcname) +{ + if (wattrset(self->win, attr) == ERR) { + curses_window_set_error(self, "wattrset", funcname); + return -1; + } + return 0; +} + /*[clinic input] _curses.window.addstr @@ -1089,7 +1110,10 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, } if (use_attr) { attr_old = getattrs(self->win); - (void)wattrset(self->win,attr); + if (curses_wattrset(self, attr, "addstr") < 0) { + curses_release_wstr(strtype, wstr); + return NULL; + } } #ifdef HAVE_NCURSESW if (strtype == 2) { @@ -1117,9 +1141,15 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, } Py_DECREF(bytesobj); } - if (use_attr) - (void)wattrset(self->win,attr_old); - return curses_window_check_err(self, rtn, funcname, "addstr"); + if (rtn == ERR) { + curses_window_set_error(self, funcname, "addstr"); + return NULL; + } + if (use_attr) { + rtn = wattrset(self->win, attr_old); + return curses_window_check_err(self, rtn, "wattrset", "addstr"); + } + Py_RETURN_NONE; } /*[clinic input] @@ -1178,7 +1208,10 @@ _curses_window_addnstr_impl(PyCursesWindowObject *self, int group_left_1, if (use_attr) { attr_old = getattrs(self->win); - (void)wattrset(self->win,attr); + if (curses_wattrset(self, attr, "addnstr") < 0) { + curses_release_wstr(strtype, wstr); + return NULL; + } } #ifdef HAVE_NCURSESW if (strtype == 2) { @@ -1206,9 +1239,15 @@ _curses_window_addnstr_impl(PyCursesWindowObject *self, int group_left_1, } Py_DECREF(bytesobj); } - if (use_attr) - (void)wattrset(self->win,attr_old); - return curses_window_check_err(self, rtn, funcname, "addnstr"); + if (rtn == ERR) { + curses_window_set_error(self, funcname, "addnstr"); + return NULL; + } + if (use_attr) { + rtn = wattrset(self->win, attr_old); + return curses_window_check_err(self, rtn, "wattrset", "addnstr"); + } + Py_RETURN_NONE; } /*[clinic input] @@ -2138,7 +2177,10 @@ _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1, if (use_attr) { attr_old = getattrs(self->win); - (void)wattrset(self->win, (attr_t)attr); + if (curses_wattrset(self, attr, "insstr") < 0) { + curses_release_wstr(strtype, wstr); + return NULL; + } } #ifdef HAVE_NCURSESW if (strtype == 2) { @@ -2166,9 +2208,15 @@ _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1, } Py_DECREF(bytesobj); } - if (use_attr) - (void)wattrset(self->win,attr_old); - return curses_window_check_err(self, rtn, funcname, "insstr"); + if (rtn == ERR) { + curses_window_set_error(self, funcname, "insstr"); + return NULL; + } + if (use_attr) { + rtn = wattrset(self->win, attr_old); + return curses_window_check_err(self, rtn, "wattrset", "insstr"); + } + Py_RETURN_NONE; } /*[clinic input] @@ -2229,7 +2277,10 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1, if (use_attr) { attr_old = getattrs(self->win); - (void)wattrset(self->win, (attr_t)attr); + if (curses_wattrset(self, attr, "insnstr") < 0) { + curses_release_wstr(strtype, wstr); + return NULL; + } } #ifdef HAVE_NCURSESW if (strtype == 2) { @@ -2257,9 +2308,15 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1, } Py_DECREF(bytesobj); } - if (use_attr) - (void)wattrset(self->win,attr_old); - return curses_window_check_err(self, rtn, funcname, "insnstr"); + if (rtn == ERR) { + curses_window_set_error(self, funcname, "insnstr"); + return NULL; + } + if (use_attr) { + rtn = wattrset(self->win, attr_old); + return curses_window_check_err(self, rtn, "wattrset", "insnstr"); + } + Py_RETURN_NONE; } /*[clinic input] From b8cc42882dd206b00d36f7ffb475865784e8a1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:15:22 +0200 Subject: [PATCH 04/23] do not suppress errors in `wborder` --- Modules/_cursesmodule.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 7a0def9c6e92ac..fb27c28bf3c7be 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1389,7 +1389,7 @@ _curses_window_border_impl(PyCursesWindowObject *self, PyObject *ls, /*[clinic end generated code: output=670ef38d3d7c2aa3 input=e015f735d67a240b]*/ { chtype ch[8]; - int i; + int i, rtn; /* Clear the array of parameters */ for(i=0; i<8; i++) @@ -1410,10 +1410,10 @@ _curses_window_border_impl(PyCursesWindowObject *self, PyObject *ls, #undef CONVERTTOCHTYPE - wborder(self->win, - ch[0], ch[1], ch[2], ch[3], - ch[4], ch[5], ch[6], ch[7]); - Py_RETURN_NONE; + rtn = wborder(self->win, + ch[0], ch[1], ch[2], ch[3], + ch[4], ch[5], ch[6], ch[7]); + return curses_window_check_err(self, rtn, "wborder", "border"); } /*[clinic input] From 61f5e06a28a22ce2a41ca0b453db6eaf69e04f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:15:44 +0200 Subject: [PATCH 05/23] do not suppress errors in `box` --- Modules/_cursesmodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index fb27c28bf3c7be..968c3358d34fe2 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1447,8 +1447,7 @@ _curses_window_box_impl(PyCursesWindowObject *self, int group_right_1, return NULL; } } - box(self->win,ch1,ch2); - Py_RETURN_NONE; + return curses_window_check_err(self, box(self->win,ch1,ch2), "box", NULL); } #if defined(HAVE_NCURSES_H) || defined(MVWDELCH_IS_EXPRESSION) From e618050c6cf3a457f254a7e15665b80a1caa69d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:16:01 +0200 Subject: [PATCH 06/23] do not suppress errors in `touchline` --- Modules/_cursesmodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 968c3358d34fe2..0c48a3cf3111b4 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1545,15 +1545,18 @@ PyCursesWindow_ChgAt(PyObject *op, PyObject *args) if (use_xy) { rtn = mvwchgat(self->win,y,x,num,attr,color,NULL); - touchline(self->win,y,1); funcname = "mvwchgat"; } else { getyx(self->win,y,x); rtn = wchgat(self->win,num,attr,color,NULL); - touchline(self->win,y,1); funcname = "wchgat"; } - return curses_window_check_err(self, rtn, funcname, "chgat"); + if (rtn == ERR) { + curses_window_set_error(self, funcname, "chgat"); + return NULL; + } + rtn = touchline(self->win,y,1); + return curses_window_check_err(self, rtn, "touchline", "chgat"); } #endif From f4927655a988c3963556a671e132b52cfe8ee841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:16:33 +0200 Subject: [PATCH 07/23] do not suppress errors in `getbkgd` --- Modules/_cursesmodule.c | 13 +++++++++---- Modules/clinic/_cursesmodule.c.h | 16 +++------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 0c48a3cf3111b4..04425f86f73ccd 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1693,16 +1693,21 @@ _curses_window_enclose_impl(PyCursesWindowObject *self, int y, int x) #endif /*[clinic input] -_curses.window.getbkgd -> long +_curses.window.getbkgd Return the window's current background character/attribute pair. [clinic start generated code]*/ -static long +static PyObject * _curses_window_getbkgd_impl(PyCursesWindowObject *self) -/*[clinic end generated code: output=c52b25dc16b215c3 input=a69db882fa35426c]*/ +/*[clinic end generated code: output=3ff953412b0e6028 input=7cf1f59a31f89df4]*/ { - return (long) getbkgd(self->win); + chtype rtn = getbkgd(self->win); + if (rtn == (chtype)ERR) { + curses_window_set_error(self, "getbkgd", NULL); + return NULL; + } + return PyLong_FromLong(rtn); } /*[clinic input] diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 552360eb80a545..297ec5d0b70fcd 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -733,23 +733,13 @@ PyDoc_STRVAR(_curses_window_getbkgd__doc__, #define _CURSES_WINDOW_GETBKGD_METHODDEF \ {"getbkgd", (PyCFunction)_curses_window_getbkgd, METH_NOARGS, _curses_window_getbkgd__doc__}, -static long +static PyObject * _curses_window_getbkgd_impl(PyCursesWindowObject *self); static PyObject * _curses_window_getbkgd(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyObject *return_value = NULL; - long _return_value; - - _return_value = _curses_window_getbkgd_impl((PyCursesWindowObject *)self); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; - } - return_value = PyLong_FromLong(_return_value); - -exit: - return return_value; + return _curses_window_getbkgd_impl((PyCursesWindowObject *)self); } PyDoc_STRVAR(_curses_window_getch__doc__, @@ -4440,4 +4430,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=42b2923d88c8d0f6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2063e1379ea08436 input=a9049054013a1b77]*/ From 192841890ee333d45b334cd7ca9c79d5a687c9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:16:47 +0200 Subject: [PATCH 08/23] do not suppress errors in `mvwinch` --- Modules/_cursesmodule.c | 21 +++++++++++++++------ Modules/clinic/_cursesmodule.c.h | 11 +++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 04425f86f73ccd..b802ac30370d11 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2033,7 +2033,7 @@ _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1, } /*[clinic input] -_curses.window.inch -> unsigned_long +_curses.window.inch [ y: int @@ -2048,21 +2048,30 @@ Return the character at the given position in the window. The bottom 8 bits are the character proper, and upper bits are the attributes. [clinic start generated code]*/ -static unsigned long +static PyObject * _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1, int y, int x) -/*[clinic end generated code: output=6c4719fe978fe86a input=fac23ee11e3b3a66]*/ +/*[clinic end generated code: output=97ca8581baaafd06 input=4b4fb43d85b177c3]*/ { - unsigned long rtn; + chtype rtn; + const char *funcname; if (!group_right_1) { + // winch() should never return (chtype)ERR, but this cannot + // be guaranteed since this is an implementation detail. rtn = winch(self->win); + funcname = "winch"; } else { + // mvwinch() may return ERR (as a chtype) if the move fails. rtn = mvwinch(self->win, y, x); + funcname = "mvwinch"; } - - return rtn; + if (rtn == (chtype)ERR) { + curses_window_set_error(self, funcname, "inch"); + return NULL; + } + return PyLong_FromUnsignedLong(rtn); } /*[-clinic input] diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 297ec5d0b70fcd..64bb72a52a7563 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -1045,7 +1045,7 @@ PyDoc_STRVAR(_curses_window_inch__doc__, #define _CURSES_WINDOW_INCH_METHODDEF \ {"inch", (PyCFunction)_curses_window_inch, METH_VARARGS, _curses_window_inch__doc__}, -static unsigned long +static PyObject * _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1, int y, int x); @@ -1056,7 +1056,6 @@ _curses_window_inch(PyObject *self, PyObject *args) int group_right_1 = 0; int y = 0; int x = 0; - unsigned long _return_value; switch (PyTuple_GET_SIZE(args)) { case 0: @@ -1071,11 +1070,7 @@ _curses_window_inch(PyObject *self, PyObject *args) PyErr_SetString(PyExc_TypeError, "_curses.window.inch requires 0 to 2 arguments"); goto exit; } - _return_value = _curses_window_inch_impl((PyCursesWindowObject *)self, group_right_1, y, x); - if ((_return_value == (unsigned long)-1) && PyErr_Occurred()) { - goto exit; - } - return_value = PyLong_FromUnsignedLong(_return_value); + return_value = _curses_window_inch_impl((PyCursesWindowObject *)self, group_right_1, y, x); exit: return return_value; @@ -4430,4 +4425,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=2063e1379ea08436 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bd8ee1ccffeda5a7 input=a9049054013a1b77]*/ From e917b9d50fd7976fa165551b268ee5e3340f0c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:17:02 +0200 Subject: [PATCH 09/23] do not suppress errors in `wrefresh` --- Modules/_cursesmodule.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index b802ac30370d11..1299e26bf3213d 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3669,8 +3669,12 @@ _curses_initscr_impl(PyObject *module) WINDOW *win; if (curses_initscr_called) { - wrefresh(stdscr); cursesmodule_state *state = get_cursesmodule_state(module); + int code = wrefresh(stdscr); + if (code == ERR) { + _curses_set_null_error(state, "wrefresh", "initscr"); + return NULL; + } return PyCursesWindow_New(state, stdscr, NULL, NULL); } From 26d5ff40a9d4d65a975ba63f933aaa6f7de2bd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:02:05 +0200 Subject: [PATCH 10/23] add note for `wbkgdset` --- Modules/_cursesmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 1299e26bf3213d..38b7d62fdf13d9 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1348,6 +1348,7 @@ _curses_window_bkgdset_impl(PyCursesWindowObject *self, PyObject *ch, if (!PyCurses_ConvertToChtype(self, ch, &bkgd)) return NULL; + // wbkgdset() is a void function wbkgdset(self->win, bkgd | attr); Py_RETURN_NONE; } From 5282d5970cefd24a163e1543082451e3e7dd456c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:02:55 +0200 Subject: [PATCH 11/23] add note for `wenclose` --- Modules/_cursesmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 38b7d62fdf13d9..e1732ea34cf3c9 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1689,6 +1689,7 @@ static PyObject * _curses_window_enclose_impl(PyCursesWindowObject *self, int y, int x) /*[clinic end generated code: output=8679beef50502648 input=4fd3355d723f7bc9]*/ { + // wenclose() returns always return a boolean return PyBool_FromLong(wenclose(self->win, y, x)); } #endif From ac5f4466f5ae7e8474f2199cea018a38625f602a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:18:30 +0200 Subject: [PATCH 12/23] do not suppress errors in `unctrl` --- Modules/_cursesmodule.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index e1732ea34cf3c9..8a0ba260ce0a10 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4860,7 +4860,12 @@ _curses_unctrl(PyObject *module, PyObject *ch) if (!PyCurses_ConvertToChtype(NULL, ch, &ch_)) return NULL; - return PyBytes_FromString(unctrl(ch_)); + const char *res = unctrl(ch_); + if (res == NULL) { + curses_set_null_error(module, "unctrl", NULL); + return NULL; + } + return PyBytes_FromString(res); } /*[clinic input] From debb159a2539ecf0c6a94f7a4c9c8991bd06817d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:19:31 +0200 Subject: [PATCH 13/23] add add note for `use_env` --- Modules/_cursesmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 8a0ba260ce0a10..bcb9ca5d635207 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4985,6 +4985,7 @@ static PyObject * _curses_use_env_impl(PyObject *module, int flag) /*[clinic end generated code: output=b2c445e435c0b164 input=06ac30948f2d78e4]*/ { + // use_env() is a void function use_env(flag); Py_RETURN_NONE; } From 29f245064c1064d083c4aa3a75ddb70d4c38ee4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:20:48 +0200 Subject: [PATCH 14/23] simplify logic in `assume_default_colors` --- Modules/_cursesmodule.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index bcb9ca5d635207..a602c302e03b4f 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -5035,13 +5035,7 @@ _curses_assume_default_colors_impl(PyObject *module, int fg, int bg) PyCursesStatefulInitialisedColor(module); code = assume_default_colors(fg, bg); - if (code != ERR) { - Py_RETURN_NONE; - } else { - cursesmodule_state *state = get_cursesmodule_state(module); - PyErr_SetString(state->error, "assume_default_colors() returned ERR"); - return NULL; - } + return curses_check_err(module, code, "assume_default_colors", NULL); } #endif /* STRICT_SYSV_CURSES */ From f0a5548e7302225c90475e01da7930e9a25e2658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:56:13 +0200 Subject: [PATCH 15/23] raise `PyExc_TypeError` instead of `curses.error` for invalid calls --- Modules/_cursesmodule.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index a602c302e03b4f..0b750927db7d71 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2410,8 +2410,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self) #ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { - cursesmodule_state *state = get_cursesmodule_state_by_win(self); - PyErr_SetString(state->error, + PyErr_SetString(PyExc_TypeError, "noutrefresh() called for a pad " "requires 6 arguments"); return NULL; @@ -2637,8 +2636,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, #ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { - cursesmodule_state *state = get_cursesmodule_state_by_win(self); - PyErr_SetString(state->error, + PyErr_SetString(PyExc_TypeError, "refresh() for a pad requires 6 arguments"); return NULL; } From 8f3b1a2f4cdaa64420822fb0e0cea6fe35073b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 20 May 2025 13:56:24 +0200 Subject: [PATCH 16/23] check return codes in auto-generated functions --- Modules/_cursesmodule.c | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 0b750927db7d71..95b234de4a53bc 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3016,12 +3016,15 @@ static PyType_Spec PyCursesWindow_Type_spec = { * * These macros should only be used for generating the body of * the module's methods since they need a module reference. + * + * The Python function name must be the same as the curses function name (X). */ -#define NoArgNoReturnFunctionBody(X) \ -{ \ - PyCursesStatefulInitialised(module); \ - return curses_check_err(module, X(), # X, NULL); } +#define NoArgNoReturnFunctionBody(X) \ +{ \ + PyCursesStatefulInitialised(module); \ + return curses_check_err(module, X(), # X, NULL); \ +} #define NoArgOrFlagNoReturnFunctionBody(X, FLAG) \ { \ @@ -3039,26 +3042,40 @@ static PyType_Spec PyCursesWindow_Type_spec = { return curses_check_err(module, rtn, funcname, # X); \ } -#define NoArgReturnIntFunctionBody(X) \ -{ \ - PyCursesStatefulInitialised(module); \ - return PyLong_FromLong((long) X()); } +#define NoArgReturnIntFunctionBody(X) \ +{ \ + PyCursesStatefulInitialised(module); \ + int rtn = X(); \ + if (rtn == ERR) { \ + curses_set_error(module, # X, NULL); \ + return NULL; \ + } \ + return PyLong_FromLong(rtn); \ +} -#define NoArgReturnStringFunctionBody(X) \ -{ \ - PyCursesStatefulInitialised(module); \ - return PyBytes_FromString(X()); } +#define NoArgReturnStringFunctionBody(X) \ +{ \ + PyCursesStatefulInitialised(module); \ + const char *res = X(); \ + if (res == NULL) { \ + curses_set_null_error(module, # X, NULL); \ + return NULL; \ + } \ + return PyBytes_FromString(res); \ +} -#define NoArgTrueFalseFunctionBody(X) \ -{ \ - PyCursesStatefulInitialised(module); \ - return PyBool_FromLong(X()); } +#define NoArgTrueFalseFunctionBody(X) \ +{ \ + PyCursesStatefulInitialised(module); \ + return PyBool_FromLong(X()); \ +} -#define NoArgNoReturnVoidFunctionBody(X) \ -{ \ - PyCursesStatefulInitialised(module); \ - X(); \ - Py_RETURN_NONE; } +#define NoArgNoReturnVoidFunctionBody(X) \ +{ \ + PyCursesStatefulInitialised(module); \ + X(); \ + Py_RETURN_NONE; \ +} /********************************************************************* Global Functions From 4a9cdb20e20ebcce482adb1c33eedab0635d5712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 23 May 2025 14:29:36 +0200 Subject: [PATCH 17/23] improve error report in delwin() --- Modules/_cursesmodule.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 95b234de4a53bc..fbe70b0d8f2897 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -933,7 +933,7 @@ PyCursesWindow_dealloc(PyObject *self) PyCursesWindowObject *wo = (PyCursesWindowObject *)self; if (wo->win != stdscr && wo->win != NULL) { if (delwin(wo->win) == ERR) { - PyErr_FormatUnraisable("delwin() error ignored"); + PyErr_FormatUnraisable("Exception ignored in delwin()"); } } if (wo->encoding != NULL) { @@ -1348,7 +1348,6 @@ _curses_window_bkgdset_impl(PyCursesWindowObject *self, PyObject *ch, if (!PyCurses_ConvertToChtype(self, ch, &bkgd)) return NULL; - // wbkgdset() is a void function wbkgdset(self->win, bkgd | attr); Py_RETURN_NONE; } @@ -1689,7 +1688,6 @@ static PyObject * _curses_window_enclose_impl(PyCursesWindowObject *self, int y, int x) /*[clinic end generated code: output=8679beef50502648 input=4fd3355d723f7bc9]*/ { - // wenclose() returns always return a boolean return PyBool_FromLong(wenclose(self->win, y, x)); } #endif @@ -5000,7 +4998,6 @@ static PyObject * _curses_use_env_impl(PyObject *module, int flag) /*[clinic end generated code: output=b2c445e435c0b164 input=06ac30948f2d78e4]*/ { - // use_env() is a void function use_env(flag); Py_RETURN_NONE; } From be1f360d39d6a0b4906876a04f8a324b859269ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Fri, 23 May 2025 14:30:43 +0200 Subject: [PATCH 18/23] remove useless comments --- Modules/_cursesmodule.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index fbe70b0d8f2897..e7001d56732c3d 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -2057,13 +2057,10 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1, const char *funcname; if (!group_right_1) { - // winch() should never return (chtype)ERR, but this cannot - // be guaranteed since this is an implementation detail. rtn = winch(self->win); funcname = "winch"; } else { - // mvwinch() may return ERR (as a chtype) if the move fails. rtn = mvwinch(self->win, y, x); funcname = "mvwinch"; } From 78976e8d181c7245a752668fe1d785e8e8d202f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 24 May 2025 23:44:01 +0200 Subject: [PATCH 19/23] Update Modules/_cursesmodule.c Co-authored-by: Victor Stinner --- Modules/_cursesmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index e7001d56732c3d..92144f530803cf 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1447,7 +1447,7 @@ _curses_window_box_impl(PyCursesWindowObject *self, int group_right_1, return NULL; } } - return curses_window_check_err(self, box(self->win,ch1,ch2), "box", NULL); + return curses_window_check_err(self, box(self->win, ch1, ch2), "box", NULL); } #if defined(HAVE_NCURSES_H) || defined(MVWDELCH_IS_EXPRESSION) From a21b8206c3ea3d3d12aefa4f46acdbcb9dced97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 25 May 2025 09:50:50 +0200 Subject: [PATCH 20/23] correctly use `PyErr_FormatUnraisable` --- Modules/_cursesmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 92144f530803cf..6856c882ff6e6e 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -933,6 +933,7 @@ PyCursesWindow_dealloc(PyObject *self) PyCursesWindowObject *wo = (PyCursesWindowObject *)self; if (wo->win != stdscr && wo->win != NULL) { if (delwin(wo->win) == ERR) { + curses_window_set_error(wo, "delwin", "__del__"); PyErr_FormatUnraisable("Exception ignored in delwin()"); } } From 5eef99f903908d0abf63955fdfc8969d762b7179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 27 May 2025 11:30:56 +0200 Subject: [PATCH 21/23] add NEWS entry --- .../2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst | 8 ++++++++ .../2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst | 3 +++ .../2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst | 7 +++++++ 3 files changed, 18 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst create mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst create mode 100644 Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst new file mode 100644 index 00000000000000..1ed90f8c7266a2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst @@ -0,0 +1,8 @@ +:class:`curses.window`: Consistently report failures of curses C API calls +in :class:`~curses.window` methods by raising a :exc:`curses.error`. This +affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, +:meth:`~curses.window.addstr`, :meth:`~curses.window.border`, +:meth:`~curses.window.box`, :meth:`~curses.window.chgat`, +:meth:`~curses.window.getbkgd`, :meth:`~curses.window.inch`, +:meth:`~curses.window.insstr` and :meth:`~curses.window.insnstr`. +Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst new file mode 100644 index 00000000000000..aaaf642efdcec2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst @@ -0,0 +1,3 @@ +:meth:`curses.window.refresh` and :meth:`curses.window.noutrefresh` now raise +a :exc:`TypeError` instead of :exc:`curses.error` when called with an incorrect +number of arguments for :ref:`pad `. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst new file mode 100644 index 00000000000000..552b7ca1a7187d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst @@ -0,0 +1,7 @@ +:mod:`curses`: Consistently report failures of curses C API calls in +module-level methods by raising a :exc:`curses.error`. This affects +:func:`~curses.assume_default_colors`, :func:`~curses.baudrate`, +:func:`~curses.cbreak`, :func:`~curses.echo`, :func:`~curses.longname`, +:func:`~curses.initscr`, :func:`~curses.nl`, :func:`~curses.raw`, +:func:`~curses.termattrs`, :func:`~curses.termname` and :func:`~curses.unctrl`. +Patch by Bénédikt Tran. From 19201deb61666449e9b95a6f4e368438815cc183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 27 May 2025 11:43:26 +0200 Subject: [PATCH 22/23] fixup refs --- Doc/howto/curses.rst | 2 ++ .../Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst index 6994a5328e8149..816639552d7cd6 100644 --- a/Doc/howto/curses.rst +++ b/Doc/howto/curses.rst @@ -161,6 +161,8 @@ your terminal won't be left in a funny state on exception and you'll be able to read the exception's message and traceback. +.. _windows-and-pads: + Windows and Pads ================ diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst index 1ed90f8c7266a2..129d5d9842576b 100644 --- a/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst +++ b/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst @@ -1,6 +1,6 @@ -:class:`curses.window`: Consistently report failures of curses C API calls -in :class:`~curses.window` methods by raising a :exc:`curses.error`. This -affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, +:ref:`curses.window `: Consistently report failures +of curses C API calls in Window methods by raising a :exc:`curses.error`. +This affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, :meth:`~curses.window.addstr`, :meth:`~curses.window.border`, :meth:`~curses.window.box`, :meth:`~curses.window.chgat`, :meth:`~curses.window.getbkgd`, :meth:`~curses.window.inch`, From eb2670cfce59fde2846f140badfb29693976cd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Tue, 27 May 2025 11:48:38 +0200 Subject: [PATCH 23/23] fixup --- .../next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst index aaaf642efdcec2..e0ef959f125d62 100644 --- a/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst +++ b/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst @@ -1,3 +1,3 @@ :meth:`curses.window.refresh` and :meth:`curses.window.noutrefresh` now raise a :exc:`TypeError` instead of :exc:`curses.error` when called with an incorrect -number of arguments for :ref:`pad `. Patch by Bénédikt Tran. +number of arguments for :ref:`pads `. Patch by Bénédikt Tran.