From 204946986feee7bc80b233350377d24d20fcb1b8 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 11 Feb 2022 17:22:11 +0100 Subject: [PATCH] bpo-46613: Add PyType_GetModuleByDef to the public API (GH-31081) * Make PyType_GetModuleByDef public (remove underscore) Co-authored-by: Victor Stinner --- Doc/c-api/type.rst | 17 +++++++++++++++++ Doc/howto/clinic.rst | 6 +++--- Doc/whatsnew/3.11.rst | 5 +++++ Include/cpython/object.h | 2 +- Lib/test/test_capi.py | 4 ++-- .../2022-02-02-17-58-49.bpo-46613.__ZdpH.rst | 2 ++ Modules/_csv.c | 2 +- Modules/_functoolsmodule.c | 2 +- Modules/_queuemodule.c | 2 +- Modules/_randommodule.c | 2 +- Modules/_sqlite/module.h | 2 +- Modules/_ssl.c | 4 ++-- Modules/_ssl.h | 2 +- Modules/_struct.c | 2 +- Modules/_testmultiphase.c | 12 ++++++------ Modules/_threadmodule.c | 6 +++--- Modules/arraymodule.c | 2 +- Modules/cjkcodecs/multibytecodec.c | 2 +- Modules/clinic/_testmultiphase.c.h | 6 +++--- Objects/typeobject.c | 8 ++------ Python/Python-tokenize.c | 2 +- 21 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2022-02-02-17-58-49.bpo-46613.__ZdpH.rst diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 97a818ab2cc..e6a5a0ea9c1 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -149,6 +149,8 @@ Type Objects ``Py_TYPE(self)`` may be a *subclass* of the intended class, and subclasses are not necessarily defined in the same module as their superclass. See :c:type:`PyCMethod` to get the class that defines the method. + See :c:func:`PyType_GetModuleByDef` for cases when ``PyCMethod`` cannot + be used. .. versionadded:: 3.9 @@ -166,6 +168,21 @@ Type Objects .. versionadded:: 3.9 +.. c:function:: PyObject* PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) + + Find the first superclass whose module was created from + the given :c:type:`PyModuleDef` *def*, and return that module. + + If no module is found, raises a :py:class:`TypeError` and returns ``NULL``. + + This function is intended to be used together with + :c:func:`PyModule_GetState()` to get module state from slot methods (such as + :c:member:`~PyTypeObject.tp_init` or :c:member:`~PyNumberMethods.nb_add`) + and other places where a method's defining class cannot be passed using the + :c:type:`PyCMethod` calling convention. + + .. versionadded:: 3.11 + Creating Heap-Allocated Types ............................. diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst index a3c4330296b..04b1a2cac0b 100644 --- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -1249,15 +1249,15 @@ The ``defining_class`` converter is not compatible with ``__init__`` and ``__new methods, which cannot use the ``METH_METHOD`` convention. It is not possible to use ``defining_class`` with slot methods. In order to -fetch the module state from such methods, use ``_PyType_GetModuleByDef`` to -look up the module and then :c:func:`PyModule_GetState` to fetch the module +fetch the module state from such methods, use :c:func:`PyType_GetModuleByDef` +to look up the module and then :c:func:`PyModule_GetState` to fetch the module state. Example from the ``setattro`` slot method in ``Modules/_threadmodule.c``:: static int local_setattro(localobject *self, PyObject *name, PyObject *v) { - PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module); + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); thread_module_state *state = get_thread_state(module); ... } diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 5738745ba13..95fce1e8e10 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -708,6 +708,11 @@ New Features (Contributed by Christian Heimes in :issue:`45459`.) +* Added the :c:data:`PyType_GetModuleByDef` function, used to get the module + in which a method was defined, in cases where this information is not + available directly (via :c:type:`PyCMethod`). + (Contributed by Petr Viktorin in :issue:`46613`.) + Porting to Python 3.11 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index a3632cf1dcf..2ee97ba0b40 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -265,7 +265,7 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject * PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *); PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *); struct PyModuleDef; -PyAPI_FUNC(PyObject *) _PyType_GetModuleByDef(PyTypeObject *, struct PyModuleDef *); +PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, struct PyModuleDef *); struct _Py_Identifier; PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 089088d97a6..8832292a999 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -1071,7 +1071,7 @@ class Test_ModuleStateAccess(unittest.TestCase): increment_count(1, 2, 3) def test_get_module_bad_def(self): - # _PyType_GetModuleByDef fails gracefully if it doesn't + # PyType_GetModuleByDef fails gracefully if it doesn't # find what it's looking for. # see bpo-46433 instance = self.module.StateAccessType() @@ -1079,7 +1079,7 @@ class Test_ModuleStateAccess(unittest.TestCase): instance.getmodulebydef_bad_def() def test_get_module_static_in_mro(self): - # Here, the class _PyType_GetModuleByDef is looking for + # Here, the class PyType_GetModuleByDef is looking for # appears in the MRO after a static type (Exception). # see bpo-46433 class Subclass(BaseException, self.module.StateAccessType): diff --git a/Misc/NEWS.d/next/C API/2022-02-02-17-58-49.bpo-46613.__ZdpH.rst b/Misc/NEWS.d/next/C API/2022-02-02-17-58-49.bpo-46613.__ZdpH.rst new file mode 100644 index 00000000000..9d0fca7a06b --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-02-02-17-58-49.bpo-46613.__ZdpH.rst @@ -0,0 +1,2 @@ +Added function :c:func:`PyType_GetModuleByDef`, which allows accesss to +module state when a method's defining class is not available. diff --git a/Modules/_csv.c b/Modules/_csv.c index d2cb77f9f03..f59d42a022e 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -374,7 +374,7 @@ static char *dialect_kws[] = { static _csvstate * _csv_state_from_type(PyTypeObject *type, const char *name) { - PyObject *module = _PyType_GetModuleByDef(type, &_csvmodule); + PyObject *module = PyType_GetModuleByDef(type, &_csvmodule); if (module == NULL) { return NULL; } diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 1f6b852f6d9..0974fc704ff 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -51,7 +51,7 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs); static inline _functools_state * get_functools_state_by_type(PyTypeObject *type) { - PyObject *module = _PyType_GetModuleByDef(type, &_functools_module); + PyObject *module = PyType_GetModuleByDef(type, &_functools_module); if (module == NULL) { return NULL; } diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 413387fecc4..af19dd6c198 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -21,7 +21,7 @@ simplequeue_get_state(PyObject *module) } static struct PyModuleDef queuemodule; #define simplequeue_get_state_by_type(type) \ - (simplequeue_get_state(_PyType_GetModuleByDef(type, &queuemodule))) + (simplequeue_get_state(PyType_GetModuleByDef(type, &queuemodule))) typedef struct { PyObject_HEAD diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 45860e342eb..d96c0371ec7 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -99,7 +99,7 @@ get_random_state(PyObject *module) static struct PyModuleDef _randommodule; #define _randomstate_type(type) \ - (get_random_state(_PyType_GetModuleByDef(type, &_randommodule))) + (get_random_state(PyType_GetModuleByDef(type, &_randommodule))) typedef struct { PyObject_HEAD diff --git a/Modules/_sqlite/module.h b/Modules/_sqlite/module.h index 1d319f1ed55..a248d044af8 100644 --- a/Modules/_sqlite/module.h +++ b/Modules/_sqlite/module.h @@ -74,7 +74,7 @@ extern struct PyModuleDef _sqlite3module; static inline pysqlite_state * pysqlite_get_state_by_type(PyTypeObject *tp) { - PyObject *module = _PyType_GetModuleByDef(tp, &_sqlite3module); + PyObject *module = PyType_GetModuleByDef(tp, &_sqlite3module); assert(module != NULL); return pysqlite_get_state(module); } diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 07f0580ef6f..d7e041fed3c 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2988,8 +2988,8 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) int result; /* slower approach, walk MRO and get borrowed reference to module. - * _PyType_GetModuleByDef is required for SSLContext subclasses */ - PyObject *module = _PyType_GetModuleByDef(type, &_sslmodule_def); + * PyType_GetModuleByDef is required for SSLContext subclasses */ + PyObject *module = PyType_GetModuleByDef(type, &_sslmodule_def); if (module == NULL) { PyErr_SetString(PyExc_RuntimeError, "Cannot find internal module state"); diff --git a/Modules/_ssl.h b/Modules/_ssl.h index 5fe6504a9dd..5593a455de4 100644 --- a/Modules/_ssl.h +++ b/Modules/_ssl.h @@ -42,7 +42,7 @@ get_ssl_state(PyObject *module) } #define get_state_type(type) \ - (get_ssl_state(_PyType_GetModuleByDef(type, &_sslmodule_def))) + (get_ssl_state(PyType_GetModuleByDef(type, &_sslmodule_def))) #define get_state_ctx(c) (((PySSLContext *)(c))->state) #define get_state_sock(s) (((PySSLSocket *)(s))->ctx->state) #define get_state_obj(o) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(o))) diff --git a/Modules/_struct.c b/Modules/_struct.c index 7d05ec5ac7c..a2e14e89d26 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -38,7 +38,7 @@ get_struct_state(PyObject *module) static struct PyModuleDef _structmodule; #define get_struct_state_structinst(self) \ - (get_struct_state(_PyType_GetModuleByDef(Py_TYPE(self), &_structmodule))) + (get_struct_state(PyType_GetModuleByDef(Py_TYPE(self), &_structmodule))) #define get_struct_state_iterinst(self) \ (get_struct_state(PyType_GetModule(Py_TYPE(self)))) diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index f7bde9895eb..4905269177b 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -136,21 +136,21 @@ _testmultiphase.StateAccessType.get_defining_module Return the module of the defining class. -Also tests that result of _PyType_GetModuleByDef matches defining_class's +Also tests that result of PyType_GetModuleByDef matches defining_class's module. [clinic start generated code]*/ static PyObject * _testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=ba2a14284a5d0921 input=356f999fc16e0933]*/ +/*[clinic end generated code: output=ba2a14284a5d0921 input=d2c7245c8a9d06f8]*/ { PyObject *retval; retval = PyType_GetModule(cls); if (retval == NULL) { return NULL; } - assert(_PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval); + assert(PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval); Py_INCREF(retval); return retval; } @@ -160,15 +160,15 @@ _testmultiphase.StateAccessType.getmodulebydef_bad_def cls: defining_class -Test that result of _PyType_GetModuleByDef with a bad def is NULL. +Test that result of PyType_GetModuleByDef with a bad def is NULL. [clinic start generated code]*/ static PyObject * _testmultiphase_StateAccessType_getmodulebydef_bad_def_impl(StateAccessTypeObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=64509074dfcdbd31 input=906047715ee293cd]*/ +/*[clinic end generated code: output=64509074dfcdbd31 input=edaff09aa4788204]*/ { - _PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise + PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise assert(PyErr_Occurred()); return NULL; } diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 072b79a75f3..ca74f9d6977 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -786,7 +786,7 @@ local_new(PyTypeObject *type, PyObject *args, PyObject *kw) } } - PyObject *module = _PyType_GetModuleByDef(type, &thread_module); + PyObject *module = PyType_GetModuleByDef(type, &thread_module); thread_module_state *state = get_thread_state(module); localobject *self = (localobject *)type->tp_alloc(type, 0); @@ -925,7 +925,7 @@ _ldict(localobject *self, thread_module_state *state) static int local_setattro(localobject *self, PyObject *name, PyObject *v) { - PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module); + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); thread_module_state *state = get_thread_state(module); PyObject *ldict = _ldict(self, state); @@ -977,7 +977,7 @@ static PyType_Spec local_type_spec = { static PyObject * local_getattro(localobject *self, PyObject *name) { - PyObject *module = _PyType_GetModuleByDef(Py_TYPE(self), &thread_module); + PyObject *module = PyType_GetModuleByDef(Py_TYPE(self), &thread_module); thread_module_state *state = get_thread_state(module); PyObject *ldict = _ldict(self, state); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 4aaab0c5314..e516f54ab61 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -67,7 +67,7 @@ get_array_state(PyObject *module) } #define find_array_state_by_type(tp) \ - (get_array_state(_PyType_GetModuleByDef(tp, &arraymodule))) + (get_array_state(PyType_GetModuleByDef(tp, &arraymodule))) #define get_array_state_by_class(cls) \ (get_array_state(PyType_GetModule(cls))) diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index df48a0deec7..acfe96654a2 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -33,7 +33,7 @@ static struct PyModuleDef _multibytecodecmodule; static _multibytecodec_state * _multibyte_codec_find_state_by_type(PyTypeObject *type) { - PyObject *module = _PyType_GetModuleByDef(type, &_multibytecodecmodule); + PyObject *module = PyType_GetModuleByDef(type, &_multibytecodecmodule); assert(module != NULL); return _multibytecodec_get_state(module); } diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h index 17c28d54ce5..b8ee93c6e19 100644 --- a/Modules/clinic/_testmultiphase.c.h +++ b/Modules/clinic/_testmultiphase.c.h @@ -8,7 +8,7 @@ PyDoc_STRVAR(_testmultiphase_StateAccessType_get_defining_module__doc__, "\n" "Return the module of the defining class.\n" "\n" -"Also tests that result of _PyType_GetModuleByDef matches defining_class\'s\n" +"Also tests that result of PyType_GetModuleByDef matches defining_class\'s\n" "module."); #define _TESTMULTIPHASE_STATEACCESSTYPE_GET_DEFINING_MODULE_METHODDEF \ @@ -39,7 +39,7 @@ PyDoc_STRVAR(_testmultiphase_StateAccessType_getmodulebydef_bad_def__doc__, "getmodulebydef_bad_def($self, /)\n" "--\n" "\n" -"Test that result of _PyType_GetModuleByDef with a bad def is NULL."); +"Test that result of PyType_GetModuleByDef with a bad def is NULL."); #define _TESTMULTIPHASE_STATEACCESSTYPE_GETMODULEBYDEF_BAD_DEF_METHODDEF \ {"getmodulebydef_bad_def", (PyCFunction)(void(*)(void))_testmultiphase_StateAccessType_getmodulebydef_bad_def, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _testmultiphase_StateAccessType_getmodulebydef_bad_def__doc__}, @@ -131,4 +131,4 @@ _testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObj exit: return return_value; } -/*[clinic end generated code: output=eb1b8c2ee6290be3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e8d074b4e6437438 input=a9049054013a1b77]*/ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index ef3549ce68f..3f8f36a9c46 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3714,13 +3714,9 @@ PyType_GetModuleState(PyTypeObject *type) /* Get the module of the first superclass where the module has the * given PyModuleDef. - * Implemented by walking the MRO, is relatively slow. - * - * This is internal API for experimentation within stdlib. Discussion: - * https://mail.python.org/archives/list/capi-sig@python.org/thread/T3P2QNLNLBRFHWSKYSTPMVEIL2EEKFJU/ */ PyObject * -_PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) +PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) { assert(PyType_Check(type)); @@ -3749,7 +3745,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) PyErr_Format( PyExc_TypeError, - "_PyType_GetModuleByDef: No superclass of '%s' has the given module", + "PyType_GetModuleByDef: No superclass of '%s' has the given module", type->tp_name); return NULL; } diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c index d3ebbe1331a..6acfc2a7cfd 100644 --- a/Python/Python-tokenize.c +++ b/Python/Python-tokenize.c @@ -13,7 +13,7 @@ get_tokenize_state(PyObject *module) { } #define _tokenize_get_state_by_type(type) \ - get_tokenize_state(_PyType_GetModuleByDef(type, &_tokenizemodule)) + get_tokenize_state(PyType_GetModuleByDef(type, &_tokenizemodule)) #include "clinic/Python-tokenize.c.h"