diff --git a/Include/py_curses.h b/Include/py_curses.h index e46b08e9cc414e..55224a8e30b73b 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -54,7 +54,7 @@ extern "C" { #endif -#define PyCurses_API_pointers 4 +#define PyCurses_API_pointers 5 /* Type declarations */ @@ -62,6 +62,7 @@ typedef struct { PyObject_HEAD WINDOW *win; char *encoding; + struct _curses_state *state; } PyCursesWindowObject; #define PyCursesWindow_Check(v) Py_IS_TYPE((v), &PyCursesWindow_Type) @@ -78,9 +79,27 @@ typedef struct { static void **PyCurses_API; #define PyCursesWindow_Type (*_PyType_CAST(PyCurses_API[0])) -#define PyCursesSetupTermCalled {if (! ((int (*)(void))PyCurses_API[1]) () ) return NULL;} -#define PyCursesInitialised {if (! ((int (*)(void))PyCurses_API[2]) () ) return NULL;} -#define PyCursesInitialisedColor {if (! ((int (*)(void))PyCurses_API[3]) () ) return NULL;} + +#define PyCursesSetupTermCalled \ + if (!(*(int *)PyCurses_API[1])) { \ + PyErr_SetString(PyCurses_API[4], \ + "must call (at least) setupterm() first"); \ + return NULL; \ + } + +#define PyCursesInitialised \ + if (!(*(int *)PyCurses_API[2])) { \ + PyErr_SetString(PyCurses_API[4], \ + "must call initscr() first"); \ + return NULL; \ + } + +#define PyCursesInitialisedColor \ + if (!(*(int *)PyCurses_API[3])) { \ + PyErr_SetString(PyCurses_API[4], \ + "must call start_color() first"); \ + return NULL; \ + } #define import_curses() \ PyCurses_API = (void **)PyCapsule_Import(PyCurses_CAPSULE_NAME, 1); diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 3ab837e4f95681..b6923a35252e24 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -1,6 +1,7 @@ import functools import inspect import os +import pickle import string import sys import tempfile @@ -1179,6 +1180,19 @@ def test_issue13051(self): # this may cause infinite recursion, leading to a RuntimeError box._insert_printable_char('a') + def test_window_non_instantiable(self): + # Ensure that '_curses.window' type in not directly instantiable + check_disallow_instantiation(self, curses.window) + + def test_window_immutable(self): + with self.assertRaisesRegex(TypeError, "immutable"): + curses.window.foo = 123 + + def test_window_non_picklable(self): + win = curses.newwin(10, 10) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertRaises(TypeError, pickle.dumps, win, proto) + class MiscTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2023-05-22-23-24-58.gh-issue-101714.iw8L88.rst b/Misc/NEWS.d/next/Library/2023-05-22-23-24-58.gh-issue-101714.iw8L88.rst new file mode 100644 index 00000000000000..465052db941021 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-22-23-24-58.gh-issue-101714.iw8L88.rst @@ -0,0 +1 @@ +Isolate :mod:`!_curses` (apply :pep:`687`). Patch by Radislav Chugunov. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 5691a419a32f8e..2197ae02d91c31 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -163,41 +163,82 @@ typedef chtype attr_t; /* No attr_t type is available */ /*[clinic input] module _curses -class _curses.window "PyCursesWindowObject *" "&PyCursesWindow_Type" +class _curses.window "PyCursesWindowObject *" "clinic_state()->PyCursesWindow_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=43265c372c2887d6]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=83369be6e20ef0da]*/ -/* Definition of exception curses.error */ -static PyObject *PyCursesError; +typedef struct _curses_state { + PyTypeObject *PyCursesWindow_Type; + PyObject *PyCursesError; + char *screen_encoding; -/* Tells whether setupterm() has been called to initialise terminfo. */ -static int initialised_setupterm = FALSE; + /* Tells whether setupterm() has been called to initialise terminfo. */ + int initialised_setupterm; + /* Tells whether initscr() has been called to initialise curses. */ + int initialised; + /* Tells whether start_color() has been called to initialise color usage. */ + int initialisedcolors; +} _curses_state; -/* Tells whether initscr() has been called to initialise curses. */ -static int initialised = FALSE; +static struct PyModuleDef _curses_module; -/* Tells whether start_color() has been called to initialise color usage. */ -static int initialisedcolors = FALSE; +static inline _curses_state * +get_curses_state(PyObject *mod) +{ + void *state = PyModule_GetState(mod); + assert (state != NULL); + return (_curses_state *)state; +} + +static inline _curses_state * +find_curses_state_by_type(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &_curses_module); + assert(mod != NULL); + return get_curses_state(mod); +} + +static int +_curses_clear(PyObject *mod) +{ + _curses_state *state = get_curses_state(mod); + Py_CLEAR(state->PyCursesWindow_Type); + Py_CLEAR(state->PyCursesError); + return 0; +} + +static int +_curses_traverse(PyObject *mod, visitproc visit, void *arg) +{ + _curses_state *state = get_curses_state(mod); + Py_VISIT(state->PyCursesWindow_Type); + Py_VISIT(state->PyCursesError); + return 0; +} -static char *screen_encoding = NULL; +static void +_curses_free(void *mod) +{ + (void)_curses_clear((PyObject *)mod); +} /* Utility Macros */ -#define PyCursesSetupTermCalled \ - if (initialised_setupterm != TRUE) { \ - PyErr_SetString(PyCursesError, \ +#define PyCursesSetupTermCalled(state) \ + if (state->initialised_setupterm != TRUE) { \ + PyErr_SetString(state->PyCursesError, \ "must call (at least) setupterm() first"); \ return 0; } -#define PyCursesInitialised \ - if (initialised != TRUE) { \ - PyErr_SetString(PyCursesError, \ +#define PyCursesInitialised(state) \ + if (state->initialised != TRUE) { \ + PyErr_SetString(state->PyCursesError, \ "must call initscr() first"); \ return 0; } -#define PyCursesInitialisedColor \ - if (initialisedcolors != TRUE) { \ - PyErr_SetString(PyCursesError, \ +#define PyCursesInitialisedColor(state) \ + if (state->initialisedcolors != TRUE) { \ + PyErr_SetString(state->PyCursesError, \ "must call start_color() first"); \ return 0; } @@ -210,15 +251,15 @@ static char *screen_encoding = NULL; */ static PyObject * -PyCursesCheckERR(int code, const char *fname) +PyCursesCheckERR(_curses_state *state, int code, const char *fname) { if (code != ERR) { Py_RETURN_NONE; } else { if (fname == NULL) { - PyErr_SetString(PyCursesError, catchall_ERR); + PyErr_SetString(state->PyCursesError, catchall_ERR); } else { - PyErr_Format(PyCursesError, "%s() returned ERR", fname); + PyErr_Format(state->PyCursesError, "%s() returned ERR", fname); } return NULL; } @@ -253,7 +294,7 @@ PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch) if (win) encoding = win->encoding; else - encoding = screen_encoding; + encoding = win->state->screen_encoding; bytes = PyUnicode_AsEncodedString(obj, encoding, NULL); if (bytes == NULL) return 0; @@ -532,23 +573,6 @@ class component_converter(CConverter): /* Function versions of the 3 functions for testing whether curses has been initialised or not. */ -static int func_PyCursesSetupTermCalled(void) -{ - PyCursesSetupTermCalled; - return 1; -} - -static int func_PyCursesInitialised(void) -{ - PyCursesInitialised; - return 1; -} - -static int func_PyCursesInitialisedColor(void) -{ - PyCursesInitialisedColor; - return 1; -} /***************************************************************************** The Window Object @@ -556,8 +580,6 @@ static int func_PyCursesInitialisedColor(void) /* Definition of the window type */ -PyTypeObject PyCursesWindow_Type; - /* Function prototype macros for Window object X - function name @@ -569,7 +591,7 @@ PyTypeObject PyCursesWindow_Type; #define Window_NoArgNoReturnFunction(X) \ static PyObject *PyCursesWindow_ ## X \ (PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) \ - { return PyCursesCheckERR(X(self->win), # X); } + { return PyCursesCheckERR(self->state, X(self->win), # X); } #define Window_NoArgTrueFalseFunction(X) \ static PyObject * PyCursesWindow_ ## X \ @@ -604,7 +626,7 @@ PyTypeObject PyCursesWindow_Type; { \ TYPE arg1; \ if (!PyArg_ParseTuple(args,PARSESTR, &arg1)) return NULL; \ - return PyCursesCheckERR(X(self->win, arg1), # X); } + return PyCursesCheckERR(self->state, X(self->win, arg1), # X); } #define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ static PyObject * PyCursesWindow_ ## X \ @@ -612,7 +634,7 @@ PyTypeObject PyCursesWindow_Type; { \ TYPE arg1, arg2; \ if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) return NULL; \ - return PyCursesCheckERR(X(self->win, arg1, arg2), # X); } + return PyCursesCheckERR(self->state, X(self->win, arg1, arg2), # X); } /* ------------- WINDOW routines --------------- */ @@ -667,7 +689,7 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") /* Allocation and deallocation of Window Objects */ static PyObject * -PyCursesWindow_New(WINDOW *win, const char *encoding) +PyCursesWindow_New(_curses_state *state, WINDOW *win, const char *encoding) { PyCursesWindowObject *wo; @@ -689,7 +711,7 @@ PyCursesWindow_New(WINDOW *win, const char *encoding) encoding = "utf-8"; } - wo = PyObject_New(PyCursesWindowObject, &PyCursesWindow_Type); + wo = PyObject_New(PyCursesWindowObject, state->PyCursesWindow_Type); if (wo == NULL) return NULL; wo->win = win; wo->encoding = _PyMem_Strdup(encoding); @@ -698,6 +720,7 @@ PyCursesWindow_New(WINDOW *win, const char *encoding) PyErr_NoMemory(); return NULL; } + wo->state = state; return (PyObject *)wo; } @@ -707,9 +730,12 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo) if (wo->win != stdscr) delwin(wo->win); if (wo->encoding != NULL) PyMem_Free(wo->encoding); - PyObject_Free(wo); + PyTypeObject *tp = Py_TYPE(wo); + tp->tp_free((PyObject *)wo); + Py_DECREF(tp); } + /* Addch, Addstr, Addnstr */ /*[clinic input] @@ -782,7 +808,7 @@ _curses_window_addch_impl(PyCursesWindowObject *self, int group_left_1, else { return NULL; } - return PyCursesCheckERR(rtn, funcname); + return PyCursesCheckERR(self->state, rtn, funcname); } /*[clinic input] @@ -862,7 +888,7 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, } if (use_attr) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, funcname); + return PyCursesCheckERR(self->state, rtn, funcname); } /*[clinic input] @@ -945,7 +971,7 @@ _curses_window_addnstr_impl(PyCursesWindowObject *self, int group_left_1, } if (use_attr) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, funcname); + return PyCursesCheckERR(self->state, rtn, funcname); } /*[clinic input] @@ -969,7 +995,7 @@ _curses_window_bkgd_impl(PyCursesWindowObject *self, PyObject *ch, long attr) if (!PyCurses_ConvertToChtype(self, ch, &bkgd)) return NULL; - return PyCursesCheckERR(wbkgd(self->win, bkgd | attr), "bkgd"); + return PyCursesCheckERR(self->state, wbkgd(self->win, bkgd | attr), "bkgd"); } /*[clinic input] @@ -985,7 +1011,7 @@ static PyObject * _curses_window_attroff_impl(PyCursesWindowObject *self, long attr) /*[clinic end generated code: output=8a2fcd4df682fc64 input=786beedf06a7befe]*/ { - return PyCursesCheckERR(wattroff(self->win, (attr_t)attr), "attroff"); + return PyCursesCheckERR(self->state, wattroff(self->win, (attr_t)attr), "attroff"); } /*[clinic input] @@ -1001,7 +1027,7 @@ static PyObject * _curses_window_attron_impl(PyCursesWindowObject *self, long attr) /*[clinic end generated code: output=7afea43b237fa870 input=5a88fba7b1524f32]*/ { - return PyCursesCheckERR(wattron(self->win, (attr_t)attr), "attron"); + return PyCursesCheckERR(self->state, wattron(self->win, (attr_t)attr), "attron"); } /*[clinic input] @@ -1017,7 +1043,7 @@ static PyObject * _curses_window_attrset_impl(PyCursesWindowObject *self, long attr) /*[clinic end generated code: output=84e379bff20c0433 input=42e400c0d0154ab5]*/ { - return PyCursesCheckERR(wattrset(self->win, (attr_t)attr), "attrset"); + return PyCursesCheckERR(self->state, wattrset(self->win, (attr_t)attr), "attrset"); } /*[clinic input] @@ -1043,7 +1069,7 @@ _curses_window_bkgdset_impl(PyCursesWindowObject *self, PyObject *ch, return NULL; wbkgdset(self->win, bkgd | attr); - return PyCursesCheckERR(0, "bkgdset"); + return PyCursesCheckERR(self->state, 0, "bkgdset"); } /*[clinic input] @@ -1241,7 +1267,7 @@ PyCursesWindow_ChgAt(PyCursesWindowObject *self, PyObject *args) rtn = wchgat(self->win,num,attr,color,NULL); touchline(self->win,y,1); } - return PyCursesCheckERR(rtn, "chgat"); + return PyCursesCheckERR(self->state, rtn, "chgat"); } #endif @@ -1265,10 +1291,10 @@ _curses_window_delch_impl(PyCursesWindowObject *self, int group_right_1, /*[clinic end generated code: output=22e77bb9fa11b461 input=d2f79e630a4fc6d0]*/ { if (!group_right_1) { - return PyCursesCheckERR(wdelch(self->win), "wdelch"); + return PyCursesCheckERR(self->state, wdelch(self->win), "wdelch"); } else { - return PyCursesCheckERR(py_mvwdelch(self->win, y, x), "mvwdelch"); + return PyCursesCheckERR(self->state, py_mvwdelch(self->win, y, x), "mvwdelch"); } } @@ -1304,11 +1330,11 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1, win = derwin(self->win,nlines,ncols,begin_y,begin_x); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(self->state->PyCursesError, catchall_NULL); return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(self->state, win, NULL); } /*[clinic input] @@ -1336,12 +1362,12 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch, #ifdef py_is_pad if (py_is_pad(self->win)) { - return PyCursesCheckERR(pechochar(self->win, ch_ | (attr_t)attr), + return PyCursesCheckERR(self->state, pechochar(self->win, ch_ | (attr_t)attr), "echochar"); } else #endif - return PyCursesCheckERR(wechochar(self->win, ch_ | (attr_t)attr), + return PyCursesCheckERR(self->state, wechochar(self->win, ch_ | (attr_t)attr), "echochar"); } @@ -1454,7 +1480,7 @@ _curses_window_getkey_impl(PyCursesWindowObject *self, int group_right_1, /* getch() returns ERR in nodelay mode */ PyErr_CheckSignals(); if (!PyErr_Occurred()) - PyErr_SetString(PyCursesError, "no input"); + PyErr_SetString(self->state->PyCursesError, "no input"); return NULL; } else if (rtn <= 255) { #ifdef NCURSES_VERSION_MAJOR @@ -1512,7 +1538,7 @@ _curses_window_get_wch_impl(PyCursesWindowObject *self, int group_right_1, return NULL; /* get_wch() returns ERR in nodelay mode */ - PyErr_SetString(PyCursesError, "no input"); + PyErr_SetString(self->state->PyCursesError, "no input"); return NULL; } if (ct == KEY_CODE_YES) @@ -1636,10 +1662,10 @@ _curses_window_hline_impl(PyCursesWindowObject *self, int group_left_1, return NULL; if (group_left_1) { if (wmove(self->win, y, x) == ERR) { - return PyCursesCheckERR(ERR, "wmove"); + return PyCursesCheckERR(self->state, ERR, "wmove"); } } - return PyCursesCheckERR(whline(self->win, ch_ | (attr_t)attr, n), "hline"); + return PyCursesCheckERR(self->state, whline(self->win, ch_ | (attr_t)attr, n), "hline"); } /*[clinic input] @@ -1686,7 +1712,7 @@ _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1, rtn = mvwinsch(self->win, y, x, ch_ | (attr_t)attr); } - return PyCursesCheckERR(rtn, "insch"); + return PyCursesCheckERR(self->state, rtn, "insch"); } /*[clinic input] @@ -1863,7 +1889,7 @@ _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1, } if (use_attr) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, funcname); + return PyCursesCheckERR(self->state, rtn, funcname); } /*[clinic input] @@ -1948,7 +1974,7 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1, } if (use_attr) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, funcname); + return PyCursesCheckERR(self->state, rtn, funcname); } /*[clinic input] @@ -2025,7 +2051,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self) #ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { - PyErr_SetString(PyCursesError, + PyErr_SetString(self->state->PyCursesError, "noutrefresh() called for a pad " "requires 6 arguments"); return NULL; @@ -2034,7 +2060,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self) rtn = pnoutrefresh(self->win, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol); Py_END_ALLOW_THREADS - return PyCursesCheckERR(rtn, "pnoutrefresh"); + return PyCursesCheckERR(self->state, rtn, "pnoutrefresh"); } if (group_right_1) { PyErr_SetString(PyExc_TypeError, @@ -2045,13 +2071,13 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self) Py_BEGIN_ALLOW_THREADS rtn = wnoutrefresh(self->win); Py_END_ALLOW_THREADS - return PyCursesCheckERR(rtn, "wnoutrefresh"); + return PyCursesCheckERR(self->state, rtn, "wnoutrefresh"); } /*[clinic input] _curses.window.overlay - destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type") + destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->PyCursesWindow_Type") [ sminrow: int @@ -2080,25 +2106,25 @@ _curses_window_overlay_impl(PyCursesWindowObject *self, PyCursesWindowObject *destwin, int group_right_1, int sminrow, int smincol, int dminrow, int dmincol, int dmaxrow, int dmaxcol) -/*[clinic end generated code: output=82bb2c4cb443ca58 input=7edd23ad22cc1984]*/ +/*[clinic end generated code: output=82bb2c4cb443ca58 input=2b1427dce85d2869]*/ { int rtn; if (group_right_1) { rtn = copywin(self->win, destwin->win, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol, TRUE); - return PyCursesCheckERR(rtn, "copywin"); + return PyCursesCheckERR(self->state, rtn, "copywin"); } else { rtn = overlay(self->win, destwin->win); - return PyCursesCheckERR(rtn, "overlay"); + return PyCursesCheckERR(self->state, rtn, "overlay"); } } /*[clinic input] _curses.window.overwrite - destwin: object(type="PyCursesWindowObject *", subclass_of="&PyCursesWindow_Type") + destwin: object(type="PyCursesWindowObject *", subclass_of="clinic_state()->PyCursesWindow_Type") [ sminrow: int @@ -2128,18 +2154,18 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self, int group_right_1, int sminrow, int smincol, int dminrow, int dmincol, int dmaxrow, int dmaxcol) -/*[clinic end generated code: output=12ae007d1681be28 input=ea5de1b35cd948e0]*/ +/*[clinic end generated code: output=12ae007d1681be28 input=ea0f756c3487e4de]*/ { int rtn; if (group_right_1) { rtn = copywin(self->win, destwin->win, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol, FALSE); - return PyCursesCheckERR(rtn, "copywin"); + return PyCursesCheckERR(self->state, rtn, "copywin"); } else { rtn = overwrite(self->win, destwin->win); - return PyCursesCheckERR(rtn, "overwrite"); + return PyCursesCheckERR(self->state, rtn, "overwrite"); } } @@ -2168,7 +2194,7 @@ _curses_window_putwin(PyCursesWindowObject *self, PyObject *file) return PyErr_SetFromErrno(PyExc_OSError); if (_Py_set_inheritable(fileno(fp), 0, NULL) < 0) goto exit; - res = PyCursesCheckERR(putwin(self->win, fp), "putwin"); + res = PyCursesCheckERR(self->state, putwin(self->win, fp), "putwin"); if (res == NULL) goto exit; fseek(fp, 0, 0); @@ -2207,7 +2233,7 @@ static PyObject * _curses_window_redrawln_impl(PyCursesWindowObject *self, int beg, int num) /*[clinic end generated code: output=ea216e334f9ce1b4 input=152155e258a77a7a]*/ { - return PyCursesCheckERR(wredrawln(self->win,beg,num), "redrawln"); + return PyCursesCheckERR(self->state, wredrawln(self->win,beg,num), "redrawln"); } /*[clinic input] @@ -2249,7 +2275,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) { - PyErr_SetString(PyCursesError, + PyErr_SetString(self->state->PyCursesError, "refresh() for a pad requires 6 arguments"); return NULL; } @@ -2257,7 +2283,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, rtn = prefresh(self->win, pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol); Py_END_ALLOW_THREADS - return PyCursesCheckERR(rtn, "prefresh"); + return PyCursesCheckERR(self->state, rtn, "prefresh"); } #endif if (group_right_1) { @@ -2268,7 +2294,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, Py_BEGIN_ALLOW_THREADS rtn = wrefresh(self->win); Py_END_ALLOW_THREADS - return PyCursesCheckERR(rtn, "prefresh"); + return PyCursesCheckERR(self->state, rtn, "prefresh"); } /*[clinic input] @@ -2290,7 +2316,7 @@ _curses_window_setscrreg_impl(PyCursesWindowObject *self, int top, int bottom) /*[clinic end generated code: output=486ab5db218d2b1a input=1b517b986838bf0e]*/ { - return PyCursesCheckERR(wsetscrreg(self->win, top, bottom), "wsetscrreg"); + return PyCursesCheckERR(self->state, wsetscrreg(self->win, top, bottom), "wsetscrreg"); } /*[clinic input] @@ -2331,11 +2357,11 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, win = subwin(self->win, nlines, ncols, begin_y, begin_x); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(self->state->PyCursesError, catchall_NULL); return NULL; } - return (PyObject *)PyCursesWindow_New(win, self->encoding); + return (PyObject *)PyCursesWindow_New(self->state, win, self->encoding); } /*[clinic input] @@ -2358,10 +2384,10 @@ _curses_window_scroll_impl(PyCursesWindowObject *self, int group_right_1, /*[clinic end generated code: output=4541a8a11852d360 input=c969ca0cfabbdbec]*/ { if (!group_right_1) { - return PyCursesCheckERR(scroll(self->win), "scroll"); + return PyCursesCheckERR(self->state, scroll(self->win), "scroll"); } else { - return PyCursesCheckERR(wscrl(self->win, lines), "scroll"); + return PyCursesCheckERR(self->state, wscrl(self->win, lines), "scroll"); } } @@ -2387,10 +2413,10 @@ _curses_window_touchline_impl(PyCursesWindowObject *self, int start, /*[clinic end generated code: output=65d05b3f7438c61d input=a98aa4f79b6be845]*/ { if (!group_right_1) { - return PyCursesCheckERR(touchline(self->win, start, count), "touchline"); + return PyCursesCheckERR(self->state, touchline(self->win, start, count), "touchline"); } else { - return PyCursesCheckERR(wtouchln(self->win, start, count, changed), "touchline"); + return PyCursesCheckERR(self->state, wtouchln(self->win, start, count, changed), "touchline"); } } @@ -2430,9 +2456,9 @@ _curses_window_vline_impl(PyCursesWindowObject *self, int group_left_1, return NULL; if (group_left_1) { if (wmove(self->win, y, x) == ERR) - return PyCursesCheckERR(ERR, "wmove"); + return PyCursesCheckERR(self->state, ERR, "wmove"); } - return PyCursesCheckERR(wvline(self->win, ch_ | (attr_t)attr, n), "vline"); + return PyCursesCheckERR(self->state, wvline(self->win, ch_ | (attr_t)attr, n), "vline"); } static PyObject * @@ -2473,7 +2499,23 @@ PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value, void *P return 0; } +/*[clinic input] +_curses.window.__reduce__ +[clinic start generated code]*/ + +static PyObject * +_curses_window___reduce___impl(PyCursesWindowObject *self) +/*[clinic end generated code: output=5d438a482619b25a input=cc7e2066b8e918b9]*/ +{ + PyErr_Format(PyExc_TypeError, + "cannot pickle %s object", + Py_TYPE(self)->tp_name); + return NULL; +} + +#define clinic_state() (find_curses_state_by_type(Py_TYPE(self))) #include "clinic/_cursesmodule.c.h" +#undef clinic_state static PyMethodDef PyCursesWindow_Methods[] = { _CURSES_WINDOW_ADDCH_METHODDEF @@ -2558,6 +2600,7 @@ static PyMethodDef PyCursesWindow_Methods[] = { {"touchwin", (PyCFunction)PyCursesWindow_touchwin, METH_NOARGS}, {"untouchwin", (PyCFunction)PyCursesWindow_untouchwin, METH_NOARGS}, _CURSES_WINDOW_VLINE_METHODDEF + _CURSES_WINDOW___REDUCE___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -2569,42 +2612,24 @@ static PyGetSetDef PyCursesWindow_getsets[] = { {NULL, NULL, NULL, NULL } /* sentinel */ }; -/* -------------------------------------------------------*/ - -PyTypeObject PyCursesWindow_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_curses.window", /*tp_name*/ - sizeof(PyCursesWindowObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - (getattrfunc)0, /*tp_getattr*/ - (setattrfunc)0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - PyCursesWindow_Methods, /*tp_methods*/ - 0, /* tp_members */ - PyCursesWindow_getsets, /* tp_getset */ + +static PyType_Slot PyCursesWindow_slots[] = { + {Py_tp_dealloc, PyCursesWindow_Dealloc}, + {Py_tp_methods, PyCursesWindow_Methods}, + {Py_tp_getset, PyCursesWindow_getsets}, + {0, NULL} +}; + + +static PyType_Spec PyCursesWindow_spec = { + .name = "_curses.window", + .basicsize = sizeof(PyCursesWindowObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .slots = PyCursesWindow_slots }; + /* Function Prototype Macros - They are ugly but very, very useful. ;-) X - function name @@ -2615,37 +2640,39 @@ PyTypeObject PyCursesWindow_Type = { #define NoArgNoReturnFunctionBody(X) \ { \ - PyCursesInitialised \ - return PyCursesCheckERR(X(), # X); } + _curses_state *state = get_curses_state(module); \ + PyCursesInitialised(state) \ + return PyCursesCheckERR(state, X(), # X); } #define NoArgOrFlagNoReturnFunctionBody(X, flag) \ { \ - PyCursesInitialised \ + _curses_state *state = get_curses_state(module); \ + PyCursesInitialised(state) \ if (flag) \ - return PyCursesCheckERR(X(), # X); \ + return PyCursesCheckERR(state, X(), # X); \ else \ - return PyCursesCheckERR(no ## X(), # X); \ + return PyCursesCheckERR(state, no ## X(), # X); \ } #define NoArgReturnIntFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised(get_curses_state(module)) \ return PyLong_FromLong((long) X()); } #define NoArgReturnStringFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised(get_curses_state(module)) \ return PyBytes_FromString(X()); } #define NoArgTrueFalseFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised(get_curses_state(module)) \ return PyBool_FromLong(X()); } #define NoArgNoReturnVoidFunctionBody(X) \ { \ - PyCursesInitialised \ + PyCursesInitialised(get_curses_state(module)) \ X(); \ Py_RETURN_NONE; } @@ -2743,11 +2770,12 @@ _curses_color_content_impl(PyObject *module, int color_number) { _CURSES_COLOR_VAL_TYPE r,g,b; - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); if (_COLOR_CONTENT_FUNC(color_number, &r, &g, &b) == ERR) { - PyErr_Format(PyCursesError, "%s() returned ERR", + PyErr_Format(state->PyCursesError, "%s() returned ERR", Py_STRINGIFY(_COLOR_CONTENT_FUNC)); return NULL; } @@ -2772,10 +2800,11 @@ static PyObject * _curses_color_pair_impl(PyObject *module, int pair_number) /*[clinic end generated code: output=60718abb10ce9feb input=6034e9146f343802]*/ { - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); - return PyLong_FromLong(COLOR_PAIR(pair_number)); + return PyLong_FromLong(COLOR_PAIR(pair_number)); } /*[clinic input] @@ -2799,10 +2828,11 @@ _curses_curs_set_impl(PyObject *module, int visibility) { int erg; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); erg = curs_set(visibility); - if (erg == ERR) return PyCursesCheckERR(erg, "curs_set"); + if (erg == ERR) return PyCursesCheckERR(state, erg, "curs_set"); return PyLong_FromLong((long) erg); } @@ -2851,9 +2881,10 @@ static PyObject * _curses_delay_output_impl(PyObject *module, int ms) /*[clinic end generated code: output=b6613a67f17fa4f4 input=5316457f5f59196c]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(delay_output(ms), "delay_output"); + return PyCursesCheckERR(state, delay_output(ms), "delay_output"); } /*[clinic input] @@ -2907,7 +2938,7 @@ _curses_erasechar_impl(PyObject *module) { char ch; - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); ch = erasechar(); @@ -2957,7 +2988,7 @@ _curses_getsyx_impl(PyObject *module) int x = 0; int y = 0; - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); getsyx(y, x); @@ -2982,11 +3013,12 @@ _curses_getmouse_impl(PyObject *module) int rtn; MEVENT event; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); rtn = getmouse( &event ); if (rtn == ERR) { - PyErr_SetString(PyCursesError, "getmouse() returned ERR"); + PyErr_SetString(state->PyCursesError, "getmouse() returned ERR"); return NULL; } return Py_BuildValue("(hiiik)", @@ -3017,14 +3049,15 @@ _curses_ungetmouse_impl(PyObject *module, short id, int x, int y, int z, { MEVENT event; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); event.id = id; event.x = x; event.y = y; event.z = z; event.bstate = bstate; - return PyCursesCheckERR(ungetmouse(&event), "ungetmouse"); + return PyCursesCheckERR(state, ungetmouse(&event), "ungetmouse"); } #endif @@ -3050,7 +3083,8 @@ _curses_getwin(PyObject *module, PyObject *file) WINDOW *win; PyObject *res = NULL; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); fp = tmpfile(); if (fp == NULL) @@ -3080,10 +3114,10 @@ _curses_getwin(PyObject *module, PyObject *file) fseek(fp, 0, 0); win = getwin(fp); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(state->PyCursesError, catchall_NULL); goto error; } - res = PyCursesWindow_New(win, NULL); + res = PyCursesWindow_New(state, win, NULL); error: fclose(fp); @@ -3106,9 +3140,10 @@ static PyObject * _curses_halfdelay_impl(PyObject *module, unsigned char tenths) /*[clinic end generated code: output=e92cdf0ef33c0663 input=e42dce7259c15100]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(halfdelay(tenths), "halfdelay"); + return PyCursesCheckERR(state, halfdelay(tenths), "halfdelay"); } /*[clinic input] @@ -3159,7 +3194,7 @@ static PyObject * _curses_has_key_impl(PyObject *module, int key) /*[clinic end generated code: output=19ad48319414d0b1 input=78bd44acf1a4997c]*/ { - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); return PyBool_FromLong(has_key(key)); } @@ -3190,10 +3225,11 @@ _curses_init_color_impl(PyObject *module, int color_number, short r, short g, short b) /*[clinic end generated code: output=d7ed71b2d818cdf2 input=ae2b8bea0f152c80]*/ { - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); - return PyCursesCheckERR(_CURSES_INIT_COLOR_FUNC(color_number, r, g, b), + return PyCursesCheckERR(state, _CURSES_INIT_COLOR_FUNC(color_number, r, g, b), Py_STRINGIFY(_CURSES_INIT_COLOR_FUNC)); } @@ -3218,8 +3254,9 @@ static PyObject * _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) /*[clinic end generated code: output=a0bba03d2bbc3ee6 input=54b421b44c12c389]*/ { - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); if (_CURSES_INIT_PAIR_FUNC(pair_number, fg, bg) == ERR) { if (pair_number >= COLOR_PAIRS) { @@ -3228,7 +3265,7 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) COLOR_PAIRS - 1); } else { - PyErr_Format(PyCursesError, "%s() returned ERR", + PyErr_Format(state->PyCursesError, "%s() returned ERR", Py_STRINGIFY(_CURSES_INIT_PAIR_FUNC)); } return NULL; @@ -3237,8 +3274,6 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) Py_RETURN_NONE; } -static PyObject *ModDict; - /*[clinic input] _curses.initscr @@ -3254,30 +3289,34 @@ _curses_initscr_impl(PyObject *module) WINDOW *win; PyCursesWindowObject *winobj; - if (initialised) { + _curses_state *state = get_curses_state(module); + + if (state->initialised) { wrefresh(stdscr); - return (PyObject *)PyCursesWindow_New(stdscr, NULL); + return (PyObject *)PyCursesWindow_New(state, stdscr, NULL); } win = initscr(); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(state->PyCursesError, catchall_NULL); return NULL; } - initialised = initialised_setupterm = TRUE; + state->initialised = TRUE; + state->initialised_setupterm = TRUE; /* This was moved from initcurses() because it core dumped on SGI, where they're not defined until you've called initscr() */ #define SetDictInt(string,ch) \ do { \ PyObject *o = PyLong_FromLong((long) (ch)); \ - if (o && PyDict_SetItemString(ModDict, string, o) == 0) { \ + if (o && PyDict_SetItemString(d, string, o) == 0) { \ Py_DECREF(o); \ } \ } while (0) + PyObject *d = PyModule_GetDict(module); /* Here are some graphic symbols you can use */ SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); @@ -3348,8 +3387,11 @@ _curses_initscr_impl(PyObject *module) SetDictInt("LINES", LINES); SetDictInt("COLS", COLS); - winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); - screen_encoding = winobj->encoding; + winobj = (PyCursesWindowObject *)PyCursesWindow_New(state, win, NULL); + if (winobj == NULL) { + return NULL; + } + state->screen_encoding = winobj->encoding; return (PyObject *)winobj; } @@ -3372,6 +3414,8 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) { int err; + _curses_state *state = get_curses_state(module); + if (fd == -1) { PyObject* sys_stdout; @@ -3379,7 +3423,7 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) if (sys_stdout == NULL || sys_stdout == Py_None) { PyErr_SetString( - PyCursesError, + state->PyCursesError, "lost sys.stdout"); return NULL; } @@ -3391,7 +3435,7 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) } } - if (!initialised_setupterm && setupterm((char *)term, fd, &err) == ERR) { + if (!state->initialised_setupterm && setupterm((char *)term, fd, &err) == ERR) { const char* s = "setupterm: unknown error"; if (err == 0) { @@ -3400,11 +3444,11 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd) s = "setupterm: could not find terminfo database"; } - PyErr_SetString(PyCursesError,s); + PyErr_SetString(state->PyCursesError, s); return NULL; } - initialised_setupterm = TRUE; + state->initialised_setupterm = TRUE; Py_RETURN_NONE; } @@ -3450,7 +3494,7 @@ _curses_set_escdelay_impl(PyObject *module, int ms) return NULL; } - return PyCursesCheckERR(set_escdelay(ms), "set_escdelay"); + return PyCursesCheckERR(get_curses_state(module), set_escdelay(ms), "set_escdelay"); } /*[clinic input] @@ -3489,7 +3533,7 @@ _curses_set_tabsize_impl(PyObject *module, int size) return NULL; } - return PyCursesCheckERR(set_tabsize(size), "set_tabsize"); + return PyCursesCheckERR(get_curses_state(module), set_tabsize(size), "set_tabsize"); } #endif @@ -3505,9 +3549,10 @@ static PyObject * _curses_intrflush_impl(PyObject *module, int flag) /*[clinic end generated code: output=c1986df35e999a0f input=c65fe2ef973fe40a]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(intrflush(NULL, flag), "intrflush"); + return PyCursesCheckERR(state, intrflush(NULL, flag), "intrflush"); } /*[clinic input] @@ -3538,7 +3583,7 @@ static PyObject * _curses_is_term_resized_impl(PyObject *module, int nlines, int ncols) /*[clinic end generated code: output=aafe04afe50f1288 input=ca9c0bd0fb8ab444]*/ { - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); return PyBool_FromLong(is_term_resized(nlines, ncols)); } @@ -3560,7 +3605,7 @@ _curses_keyname_impl(PyObject *module, int key) { const char *knp; - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); if (key < 0) { PyErr_SetString(PyExc_ValueError, "invalid key number"); @@ -3618,9 +3663,10 @@ static PyObject * _curses_meta_impl(PyObject *module, int yes) /*[clinic end generated code: output=22f5abda46a605d8 input=cfe7da79f51d0e30]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(meta(stdscr, yes), "meta"); + return PyCursesCheckERR(state, meta(stdscr, yes), "meta"); } #ifdef NCURSES_MOUSE_VERSION @@ -3642,9 +3688,10 @@ static PyObject * _curses_mouseinterval_impl(PyObject *module, int interval) /*[clinic end generated code: output=c4f5ff04354634c5 input=75aaa3f0db10ac4e]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(mouseinterval(interval), "mouseinterval"); + return PyCursesCheckERR(state, mouseinterval(interval), "mouseinterval"); } /*[clinic input] @@ -3667,7 +3714,7 @@ _curses_mousemask_impl(PyObject *module, unsigned long newmask) { mmask_t oldmask, availmask; - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); availmask = mousemask((mmask_t)newmask, &oldmask); return Py_BuildValue("(kk)", (unsigned long)availmask, (unsigned long)oldmask); @@ -3688,7 +3735,7 @@ static PyObject * _curses_napms_impl(PyObject *module, int ms) /*[clinic end generated code: output=a40a1da2e39ea438 input=20cd3af2b6900f56]*/ { - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); return Py_BuildValue("i", napms(ms)); } @@ -3712,16 +3759,17 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols) { WINDOW *win; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); win = newpad(nlines, ncols); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(state->PyCursesError, catchall_NULL); return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(state, win, NULL); } /*[clinic input] @@ -3752,15 +3800,16 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols, { WINDOW *win; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); win = newwin(nlines,ncols,begin_y,begin_x); if (win == NULL) { - PyErr_SetString(PyCursesError, catchall_NULL); + PyErr_SetString(state->PyCursesError, catchall_NULL); return NULL; } - return (PyObject *)PyCursesWindow_New(win, NULL); + return (PyObject *)PyCursesWindow_New(state, win, NULL); } /*[clinic input] @@ -3864,8 +3913,9 @@ _curses_pair_content_impl(PyObject *module, int pair_number) { _CURSES_COLOR_NUM_TYPE f, b; - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); if (_CURSES_PAIR_CONTENT_FUNC(pair_number, &f, &b) == ERR) { if (pair_number >= COLOR_PAIRS) { @@ -3874,7 +3924,7 @@ _curses_pair_content_impl(PyObject *module, int pair_number) COLOR_PAIRS - 1); } else { - PyErr_Format(PyCursesError, "%s() returned ERR", + PyErr_Format(state->PyCursesError, "%s() returned ERR", Py_STRINGIFY(_CURSES_PAIR_CONTENT_FUNC)); } return NULL; @@ -3898,8 +3948,9 @@ static PyObject * _curses_pair_number_impl(PyObject *module, int attr) /*[clinic end generated code: output=85bce7d65c0aa3f4 input=d478548e33f5e61a]*/ { - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); return PyLong_FromLong(PAIR_NUMBER(attr)); } @@ -3919,7 +3970,7 @@ static PyObject * _curses_putp_impl(PyObject *module, const char *string) /*[clinic end generated code: output=e98081d1b8eb5816 input=1601faa828b44cb3]*/ { - return PyCursesCheckERR(putp(string), "putp"); + return PyCursesCheckERR(get_curses_state(module), putp(string), "putp"); } /*[clinic input] @@ -3939,7 +3990,7 @@ static PyObject * _curses_qiflush_impl(PyObject *module, int flag) /*[clinic end generated code: output=9167e862f760ea30 input=6ec8b3e2b717ec40]*/ { - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); if (flag) { qiflush(); @@ -3954,10 +4005,11 @@ _curses_qiflush_impl(PyObject *module, int flag) * and _curses.COLS */ #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) static int -update_lines_cols(void) +update_lines_cols(PyObject *mod) { PyObject *o; PyObject *m = PyImport_ImportModule("curses"); + PyObject *d = PyModule_GetDict(mod); if (!m) return 0; @@ -3972,7 +4024,7 @@ update_lines_cols(void) Py_DECREF(o); return 0; } - if (PyDict_SetItemString(ModDict, "LINES", o)) { + if (PyDict_SetItemString(d, "LINES", o)) { Py_DECREF(m); Py_DECREF(o); return 0; @@ -3988,7 +4040,7 @@ update_lines_cols(void) Py_DECREF(o); return 0; } - if (PyDict_SetItemString(ModDict, "COLS", o)) { + if (PyDict_SetItemString(d, "COLS", o)) { Py_DECREF(m); Py_DECREF(o); return 0; @@ -4007,7 +4059,7 @@ static PyObject * _curses_update_lines_cols_impl(PyObject *module) /*[clinic end generated code: output=423f2b1e63ed0f75 input=5f065ab7a28a5d90]*/ { - if (!update_lines_cols()) { + if (!update_lines_cols(module)) { return NULL; } Py_RETURN_NONE; @@ -4089,12 +4141,13 @@ _curses_resizeterm_impl(PyObject *module, int nlines, int ncols) { PyObject *result; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - result = PyCursesCheckERR(resizeterm(nlines, ncols), "resizeterm"); + result = PyCursesCheckERR(state, resizeterm(nlines, ncols), "resizeterm"); if (!result) return NULL; - if (!update_lines_cols()) { + if (!update_lines_cols(module)) { Py_DECREF(result); return NULL; } @@ -4128,12 +4181,13 @@ _curses_resize_term_impl(PyObject *module, int nlines, int ncols) { PyObject *result; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - result = PyCursesCheckERR(resize_term(nlines, ncols), "resize_term"); + result = PyCursesCheckERR(state, resize_term(nlines, ncols), "resize_term"); if (!result) return NULL; - if (!update_lines_cols()) { + if (!update_lines_cols(module)) { Py_DECREF(result); return NULL; } @@ -4171,7 +4225,7 @@ static PyObject * _curses_setsyx_impl(PyObject *module, int y, int x) /*[clinic end generated code: output=23dcf753511a2464 input=fa7f2b208e10a557]*/ { - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); setsyx(y,x); @@ -4199,15 +4253,16 @@ _curses_start_color_impl(PyObject *module) int code; PyObject *c, *cp; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); code = start_color(); if (code != ERR) { - initialisedcolors = TRUE; + state->initialisedcolors = TRUE; c = PyLong_FromLong((long) COLORS); if (c == NULL) return NULL; - if (PyDict_SetItemString(ModDict, "COLORS", c) < 0) { + if (PyModule_AddObjectRef(module, "COLORS", c) < 0) { Py_DECREF(c); return NULL; } @@ -4215,14 +4270,14 @@ _curses_start_color_impl(PyObject *module) cp = PyLong_FromLong((long) COLOR_PAIRS); if (cp == NULL) return NULL; - if (PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp) < 0) { + if (PyModule_AddObjectRef(module, "COLOR_PAIRS", cp) < 0) { Py_DECREF(cp); return NULL; } Py_DECREF(cp); Py_RETURN_NONE; } else { - PyErr_SetString(PyCursesError, "start_color() returned ERR"); + PyErr_SetString(state->PyCursesError, "start_color() returned ERR"); return NULL; } } @@ -4266,7 +4321,7 @@ static PyObject * _curses_tigetflag_impl(PyObject *module, const char *capname) /*[clinic end generated code: output=8853c0e55542195b input=b0787af9e3e9a6ce]*/ { - PyCursesSetupTermCalled; + PyCursesSetupTermCalled(get_curses_state(module)); return PyLong_FromLong( (long) tigetflag( (char *)capname ) ); } @@ -4288,7 +4343,7 @@ static PyObject * _curses_tigetnum_impl(PyObject *module, const char *capname) /*[clinic end generated code: output=46f8b0a1b5dff42f input=5cdf2f410b109720]*/ { - PyCursesSetupTermCalled; + PyCursesSetupTermCalled(get_curses_state(module)); return PyLong_FromLong( (long) tigetnum( (char *)capname ) ); } @@ -4310,7 +4365,7 @@ static PyObject * _curses_tigetstr_impl(PyObject *module, const char *capname) /*[clinic end generated code: output=f22b576ad60248f3 input=36644df25c73c0a7]*/ { - PyCursesSetupTermCalled; + PyCursesSetupTermCalled(get_curses_state(module)); capname = tigetstr( (char *)capname ); if (capname == NULL || capname == (char*) -1) { @@ -4345,11 +4400,12 @@ _curses_tparm_impl(PyObject *module, const char *str, int i1, int i2, int i3, { char* result = NULL; - PyCursesSetupTermCalled; + _curses_state *state = get_curses_state(module); + PyCursesSetupTermCalled(state); result = tparm((char *)str,i1,i2,i3,i4,i5,i6,i7,i8,i9); if (!result) { - PyErr_SetString(PyCursesError, "tparm() returned NULL"); + PyErr_SetString(state->PyCursesError, "tparm() returned NULL"); return NULL; } @@ -4373,9 +4429,10 @@ static PyObject * _curses_typeahead_impl(PyObject *module, int fd) /*[clinic end generated code: output=084bb649d7066583 input=f2968d8e1805051b]*/ { - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); - return PyCursesCheckERR(typeahead( fd ), "typeahead"); + return PyCursesCheckERR(state, typeahead( fd ), "typeahead"); } #endif @@ -4397,7 +4454,7 @@ _curses_unctrl(PyObject *module, PyObject *ch) { chtype ch_; - PyCursesInitialised; + PyCursesInitialised(get_curses_state(module)); if (!PyCurses_ConvertToChtype(NULL, ch, &ch_)) return NULL; @@ -4420,12 +4477,13 @@ _curses_ungetch(PyObject *module, PyObject *ch) { chtype ch_; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); if (!PyCurses_ConvertToChtype(NULL, ch, &ch_)) return NULL; - return PyCursesCheckERR(ungetch(ch_), "ungetch"); + return PyCursesCheckERR(state, ungetch(ch_), "ungetch"); } #ifdef HAVE_NCURSESW @@ -4491,11 +4549,12 @@ _curses_unget_wch(PyObject *module, PyObject *ch) { wchar_t wch; - PyCursesInitialised; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); if (!PyCurses_ConvertToWchar_t(ch, &wch)) return NULL; - return PyCursesCheckERR(unget_wch(wch), "unget_wch"); + return PyCursesCheckERR(state, unget_wch(wch), "unget_wch"); } #endif @@ -4543,14 +4602,15 @@ _curses_use_default_colors_impl(PyObject *module) { int code; - PyCursesInitialised; - PyCursesInitialisedColor; + _curses_state *state = get_curses_state(module); + PyCursesInitialised(state); + PyCursesInitialisedColor(state); code = use_default_colors(); if (code != ERR) { Py_RETURN_NONE; } else { - PyErr_SetString(PyCursesError, "use_default_colors() returned ERR"); + PyErr_SetString(state->PyCursesError, "use_default_colors() returned ERR"); return NULL; } } @@ -4709,83 +4769,76 @@ static PyMethodDef PyCurses_methods[] = { {NULL, NULL} /* sentinel */ }; -/* Initialization function for the module */ - - -static struct PyModuleDef _cursesmodule = { - PyModuleDef_HEAD_INIT, - "_curses", - NULL, - -1, - PyCurses_methods, - NULL, - NULL, - NULL, - NULL -}; - static void -curses_destructor(PyObject *op) +curses_capi_destructor(PyObject *op) { void *ptr = PyCapsule_GetPointer(op, PyCurses_CAPSULE_NAME); Py_DECREF(*(void **)ptr); PyMem_Free(ptr); } -PyMODINIT_FUNC -PyInit__curses(void) +static int +_curses_exec(PyObject *module) { - PyObject *m, *d, *v, *c_api_object; + PyObject *d, *v, *error, *c_api_object; + PyTypeObject *win_tp; + + _curses_state *state = get_curses_state(module); /* Initialize object type */ - if (PyType_Ready(&PyCursesWindow_Type) < 0) - return NULL; + win_tp = (PyTypeObject *)PyType_FromModuleAndSpec(module, &PyCursesWindow_spec, NULL); + if (win_tp == NULL) + return -1; + if (PyModule_AddType(module, win_tp) < 0) + return -1; + state->PyCursesWindow_Type = win_tp; - /* Create the module and add the functions */ - m = PyModule_Create(&_cursesmodule); - if (m == NULL) - return NULL; + d = PyModule_GetDict(module); - /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - if (d == NULL) - return NULL; - ModDict = d; /* For PyCurses_InitScr to use later */ + /* For exception curses.error */ + error = PyErr_NewException("_curses.error", NULL, NULL); + if (PyDict_SetItemString(d, "error", error) < 0) { + return -1; + } + state->PyCursesError = error; + state->screen_encoding = NULL; + + state->initialised = FALSE; + state->initialised_setupterm = FALSE; + state->initialisedcolors = FALSE; + /* Allocate C API pointer array */ void **PyCurses_API = PyMem_Calloc(PyCurses_API_pointers, sizeof(void *)); if (PyCurses_API == NULL) { PyErr_NoMemory(); - return NULL; + return -1; } /* Initialize the C API pointer array */ - PyCurses_API[0] = (void *)Py_NewRef(&PyCursesWindow_Type); - PyCurses_API[1] = (void *)func_PyCursesSetupTermCalled; - PyCurses_API[2] = (void *)func_PyCursesInitialised; - PyCurses_API[3] = (void *)func_PyCursesInitialisedColor; + PyCurses_API[0] = (void *)Py_NewRef(state->PyCursesWindow_Type); + PyCurses_API[1] = (void *)&state->initialised; + PyCurses_API[2] = (void *)&state->initialised_setupterm; + PyCurses_API[3] = (void *)&state->initialisedcolors; + PyCurses_API[4] = (void *)Py_NewRef(state->PyCursesError); /* Add a capsule for the C API */ c_api_object = PyCapsule_New(PyCurses_API, PyCurses_CAPSULE_NAME, - curses_destructor); + curses_capi_destructor); if (c_api_object == NULL) { - Py_DECREF(PyCurses_API[0]); PyMem_Free(PyCurses_API); - return NULL; + return -1; } if (PyDict_SetItemString(d, "_C_API", c_api_object) < 0) { - Py_DECREF(c_api_object); - return NULL; + return -1; } - Py_DECREF(c_api_object); - - /* For exception curses.error */ - PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); - PyDict_SetItemString(d, "error", PyCursesError); /* Make the version available */ v = PyBytes_FromString(PyCursesVersion); - PyDict_SetItemString(d, "version", v); - PyDict_SetItemString(d, "__version__", v); - Py_DECREF(v); + if (PyDict_SetItemString(d, "version", v) < 0) { + return -1; + } + if (PyDict_SetItemString(d, "__version__", v) < 0) { + return -1; + } #ifdef NCURSES_VERSION /* ncurses_version */ @@ -4793,15 +4846,15 @@ PyInit__curses(void) version_type = _PyStructSequence_NewType(&ncurses_version_desc, Py_TPFLAGS_DISALLOW_INSTANTIATION); if (version_type == NULL) { - return NULL; + return -1; } v = make_ncurses_version(version_type); - Py_DECREF(version_type); if (v == NULL) { - return NULL; + return -1; + } + if (PyDict_SetItemString(d, "ncurses_version", v) < 0) { + return -1; } - PyDict_SetItemString(d, "ncurses_version", v); - Py_DECREF(v); #endif /* NCURSES_VERSION */ SetDictInt("ERR", ERR); @@ -4934,8 +4987,28 @@ PyInit__curses(void) SetDictInt("KEY_MAX", KEY_MAX); } - if (PyModule_AddType(m, &PyCursesWindow_Type) < 0) { - return NULL; - } - return m; + return 0; +} + +static struct PyModuleDef_Slot _curses_slots[] = { + {Py_mod_exec, _curses_exec}, + {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, + {0, NULL} +}; + +static struct PyModuleDef _curses_module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "_curses", + .m_size = sizeof(_curses_state), + .m_methods = PyCurses_methods, + .m_slots = _curses_slots, + .m_traverse = _curses_traverse, + .m_clear = _curses_clear, + .m_free = _curses_free +}; + +PyMODINIT_FUNC +PyInit__curses(void) +{ + return PyModuleDef_Init(&_curses_module); } diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 9d99d41af5d2d9..3e660ed546cf17 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -1389,12 +1389,12 @@ _curses_window_overlay(PyCursesWindowObject *self, PyObject *args) switch (PyTuple_GET_SIZE(args)) { case 1: - if (!PyArg_ParseTuple(args, "O!:overlay", &PyCursesWindow_Type, &destwin)) { + if (!PyArg_ParseTuple(args, "O!:overlay", clinic_state()->PyCursesWindow_Type, &destwin)) { goto exit; } break; case 7: - if (!PyArg_ParseTuple(args, "O!iiiiii:overlay", &PyCursesWindow_Type, &destwin, &sminrow, &smincol, &dminrow, &dmincol, &dmaxrow, &dmaxcol)) { + if (!PyArg_ParseTuple(args, "O!iiiiii:overlay", clinic_state()->PyCursesWindow_Type, &destwin, &sminrow, &smincol, &dminrow, &dmincol, &dmaxrow, &dmaxcol)) { goto exit; } group_right_1 = 1; @@ -1448,12 +1448,12 @@ _curses_window_overwrite(PyCursesWindowObject *self, PyObject *args) switch (PyTuple_GET_SIZE(args)) { case 1: - if (!PyArg_ParseTuple(args, "O!:overwrite", &PyCursesWindow_Type, &destwin)) { + if (!PyArg_ParseTuple(args, "O!:overwrite", clinic_state()->PyCursesWindow_Type, &destwin)) { goto exit; } break; case 7: - if (!PyArg_ParseTuple(args, "O!iiiiii:overwrite", &PyCursesWindow_Type, &destwin, &sminrow, &smincol, &dminrow, &dmincol, &dmaxrow, &dmaxcol)) { + if (!PyArg_ParseTuple(args, "O!iiiiii:overwrite", clinic_state()->PyCursesWindow_Type, &destwin, &sminrow, &smincol, &dminrow, &dmincol, &dmaxrow, &dmaxcol)) { goto exit; } group_right_1 = 1; @@ -1833,6 +1833,23 @@ _curses_window_vline(PyCursesWindowObject *self, PyObject *args) return return_value; } +PyDoc_STRVAR(_curses_window___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n"); + +#define _CURSES_WINDOW___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)_curses_window___reduce__, METH_NOARGS, _curses_window___reduce____doc__}, + +static PyObject * +_curses_window___reduce___impl(PyCursesWindowObject *self); + +static PyObject * +_curses_window___reduce__(PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _curses_window___reduce___impl(self); +} + #if defined(HAVE_CURSES_FILTER) PyDoc_STRVAR(_curses_filter__doc__, @@ -4313,4 +4330,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=27a2364193b503c1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7a49be78fe2f4adc input=a9049054013a1b77]*/