diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 9f7f8535b6d9a8..4ba210c162d0f6 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -278,6 +278,14 @@ def test_subwindows_references(self): del win2 gc_collect() + def test_getparent(self): + # getparent() calls no curses function, so it works with any backend + # and is not gated like the is_*() getters below. + stdscr = self.stdscr + self.assertIsNone(stdscr.getparent()) + sub = stdscr.subwin(3, 3, 0, 0) + self.assertIs(sub.getparent(), stdscr) + def test_dupwin(self): win = curses.newwin(5, 10, 2, 3) win.addstr(0, 0, 'ABCDE') @@ -514,6 +522,12 @@ def test_in_wstr(self): self.assertEqual(stdscr.in_wstr(0, 0, len(s)), s) self.assertIsInstance(stdscr.instr(0, 0, len(s)), bytes) + # Reading no characters gives an empty string, like instr() and + # in_wchstr() do. curses does not terminate the buffer in this case. + stdscr.addstr(0, 0, 'abz') + self.assertEqual(stdscr.in_wstr(0, 0, 0), '') + self.assertEqual(stdscr.in_wstr(0), '') + def test_complexchar(self): # A complexchar is a styled wide-character cell: str() is its text, # and the attr and pair attributes are its rendition. @@ -1771,13 +1785,11 @@ def test_state_getters(self): stdscr.setscrreg(5, 10) self.assertEqual(stdscr.getscrreg(), (5, 10)) - # is_pad()/is_subwin()/getparent(). + # is_pad()/is_subwin(). self.assertIs(stdscr.is_pad(), False) self.assertIs(stdscr.is_subwin(), False) - self.assertIsNone(stdscr.getparent()) sub = stdscr.subwin(3, 3, 0, 0) self.assertIs(sub.is_subwin(), True) - self.assertIs(sub.getparent(), stdscr) pad = curses.newpad(5, 5) self.assertIs(pad.is_pad(), True) diff --git a/Lib/test/test_free_threading/test_functions.py b/Lib/test/test_free_threading/test_functions.py index 65a90a17bd5b8d..7a59638d27dc9d 100644 --- a/Lib/test/test_free_threading/test_functions.py +++ b/Lib/test/test_free_threading/test_functions.py @@ -58,6 +58,12 @@ def test_annotate(self): def test_type_params(self): self.stress_attribute("__type_params__", lambda: (random_string(),)) + def test_doc(self): + self.stress_attribute("__doc__", random_string) + + def test_module(self): + self.stress_attribute("__module__", random_string) + def test_annotations_and_annotate(self): # The __annotations__ and __annotate__ setters clear each other. def target(): pass diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 01ea3c43cce2e6..b8680edc6c0bed 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1936,6 +1936,7 @@ PyCursesWindow_getscrreg(PyObject *op, PyObject *Py_UNUSED(ignored)) } return Py_BuildValue("(ii)", top, bottom); } +#endif /* NCURSES_EXT_FUNCS >= 20110404 || PDCURSES */ static PyObject * PyCursesWindow_getparent(PyObject *op, PyObject *Py_UNUSED(ignored)) @@ -1948,7 +1949,6 @@ PyCursesWindow_getparent(PyObject *op, PyObject *Py_UNUSED(ignored)) } return Py_NewRef((PyObject *)self->orig); } -#endif /* NCURSES_EXT_FUNCS >= 20110404 || PDCURSES */ Window_NoArgNoReturnVoidFunction(wsyncup) Window_NoArgNoReturnVoidFunction(wsyncdown) @@ -3956,7 +3956,7 @@ PyCursesWindow_in_wstr(PyObject *op, PyObject *args) PyMem_Free(buf); return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - PyObject *res = PyUnicode_FromWideChar(buf, -1); + PyObject *res = PyUnicode_FromWideChar(buf, rtn); PyMem_Free(buf); return res; #else @@ -5061,11 +5061,9 @@ static PyMethodDef PyCursesWindow_methods[] = { {"getmaxyx", PyCursesWindow_getmaxyx, METH_NOARGS, "getmaxyx($self, /)\n--\n\n" "Return a tuple (y, x) of the window height and width."}, -#if (defined(NCURSES_EXT_FUNCS) && NCURSES_EXT_FUNCS >= 20110404) || defined(PDCURSES) {"getparent", PyCursesWindow_getparent, METH_NOARGS, "getparent($self, /)\n--\n\n" "Return the parent window, or None if this is not a subwindow."}, -#endif {"getparyx", PyCursesWindow_getparyx, METH_NOARGS, "getparyx($self, /)\n--\n\n" "Return (y, x) relative to the parent window, or (-1, -1) if none."}, diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 49a28e8ad66714..0c1fab7f6d33a8 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -628,9 +628,7 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations) static PyMemberDef func_memberlist[] = { {"__closure__", _Py_T_OBJECT, OFF(func_closure), Py_READONLY}, - {"__doc__", _Py_T_OBJECT, OFF(func_doc), 0}, {"__globals__", _Py_T_OBJECT, OFF(func_globals), Py_READONLY}, - {"__module__", _Py_T_OBJECT, OFF(func_module), 0}, {"__builtins__", _Py_T_OBJECT, OFF(func_builtins), Py_READONLY}, {NULL} /* Sentinel */ }; @@ -762,6 +760,56 @@ func_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) return 0; } +static PyObject * +func_get_doc(PyObject *self, void *Py_UNUSED(ignored)) +{ + PyFunctionObject *op = _PyFunction_CAST(self); + PyObject *doc = op->func_doc; + if (doc == NULL) { + doc = Py_None; + } + return Py_NewRef(doc); +} + +static int +func_set_doc(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) +{ + /* Legal to del f.__doc__ or to set it to any object. */ + PyFunctionObject *op = _PyFunction_CAST(self); + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + PyObject *old_doc = op->func_doc; + op->func_doc = Py_XNewRef(value); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_doc); + return 0; +} + +static PyObject * +func_get_module(PyObject *self, void *Py_UNUSED(ignored)) +{ + PyFunctionObject *op = _PyFunction_CAST(self); + PyObject *module = op->func_module; + if (module == NULL) { + module = Py_None; + } + return Py_NewRef(module); +} + +static int +func_set_module(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) +{ + /* Legal to del f.__module__ or to set it to any object. */ + PyFunctionObject *op = _PyFunction_CAST(self); + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + PyObject *old_module = op->func_module; + op->func_module = Py_XNewRef(value); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_module); + return 0; +} + static PyObject * func_get_defaults(PyObject *self, void *Py_UNUSED(ignored)) { @@ -891,24 +939,12 @@ function___annotate___set_impl(PyFunctionObject *self, PyObject *value) return -1; } if (Py_IsNone(value)) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyEval_StopTheWorld(interp); - PyObject *old_annotate = self->func_annotate; - self->func_annotate = Py_NewRef(value); - _PyEval_StartTheWorld(interp); - Py_XDECREF(old_annotate); + Py_XSETREF(self->func_annotate, Py_NewRef(value)); return 0; } else if (PyCallable_Check(value)) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyEval_StopTheWorld(interp); - PyObject *old_annotate = self->func_annotate; - self->func_annotate = Py_NewRef(value); - PyObject *old_annotations = self->func_annotations; - self->func_annotations = NULL; - _PyEval_StartTheWorld(interp); - Py_XDECREF(old_annotate); - Py_XDECREF(old_annotations); + Py_XSETREF(self->func_annotate, Py_NewRef(value)); + Py_CLEAR(self->func_annotations); return 0; } else { @@ -961,15 +997,8 @@ function___annotations___set_impl(PyFunctionObject *self, PyObject *value) "__annotations__ must be set to a dict object"); return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyEval_StopTheWorld(interp); - PyObject *old_annotations = self->func_annotations; - self->func_annotations = Py_XNewRef(value); - PyObject *old_annotate = self->func_annotate; - self->func_annotate = NULL; - _PyEval_StartTheWorld(interp); - Py_XDECREF(old_annotations); - Py_XDECREF(old_annotate); + Py_XSETREF(self->func_annotations, Py_XNewRef(value)); + Py_CLEAR(self->func_annotate); return 0; } @@ -1010,12 +1039,7 @@ function___type_params___set_impl(PyFunctionObject *self, PyObject *value) "__type_params__ must be set to a tuple"); return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyEval_StopTheWorld(interp); - PyObject *old_typeparams = self->func_typeparams; - self->func_typeparams = Py_NewRef(value); - _PyEval_StartTheWorld(interp); - Py_XDECREF(old_typeparams); + Py_XSETREF(self->func_typeparams, Py_NewRef(value)); return 0; } @@ -1037,6 +1061,8 @@ static PyGetSetDef func_getsetlist[] = { FUNCTION___ANNOTATIONS___GETSETDEF FUNCTION___ANNOTATE___GETSETDEF {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, + {"__doc__", func_get_doc, func_set_doc}, + {"__module__", func_get_module, func_set_module}, {"__name__", func_get_name, func_set_name}, {"__qualname__", func_get_qualname, func_set_qualname}, FUNCTION___TYPE_PARAMS___GETSETDEF