[Python-checkins] [3.10] bpo-46433: _PyType_GetModuleByDef: handle static types in MRO (GH-30696) (GH-31262)

encukou webhook-mailer at python.org
Fri Feb 11 06:25:49 EST 2022


https://github.com/python/cpython/commit/8b8673fe940c4ebc4512bff5af180b66def3d1ae
commit: 8b8673fe940c4ebc4512bff5af180b66def3d1ae
branch: 3.10
author: Petr Viktorin <encukou at gmail.com>
committer: encukou <encukou at gmail.com>
date: 2022-02-11T12:25:25+01:00
summary:

[3.10] bpo-46433: _PyType_GetModuleByDef: handle static types in MRO (GH-30696) (GH-31262)


(cherry picked from commit 0ef08530124c5ca13a9394f4ac18bee8e6c66409)

files:
A Misc/NEWS.d/next/C API/2022-01-19-16-51-54.bpo-46433.Er9ApS.rst
M Lib/test/test_capi.py
M Modules/_testmultiphase.c
M Modules/clinic/_testmultiphase.c.h
M Objects/typeobject.c

diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index ccb9d5383d6be..7b208c90063fd 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -1030,6 +1030,22 @@ def test_state_access(self):
                 with self.assertRaises(TypeError):
                     increment_count(1, 2, 3)
 
+    def test_get_module_bad_def(self):
+        # _PyType_GetModuleByDef fails gracefully if it doesn't
+        # find what it's looking for.
+        # see bpo-46433
+        instance = self.module.StateAccessType()
+        with self.assertRaises(TypeError):
+            instance.getmodulebydef_bad_def()
+
+    def test_get_module_static_in_mro(self):
+        # 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):
+            pass
+        self.assertIs(Subclass().get_defining_module(), self.module)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/C API/2022-01-19-16-51-54.bpo-46433.Er9ApS.rst b/Misc/NEWS.d/next/C API/2022-01-19-16-51-54.bpo-46433.Er9ApS.rst
new file mode 100644
index 0000000000000..e1987c4536b5c
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-01-19-16-51-54.bpo-46433.Er9ApS.rst	
@@ -0,0 +1,2 @@
+The internal function _PyType_GetModuleByDef now correctly handles
+inheritance patterns involving static types.
diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c
index e0ed77d265cdc..58282cec1087d 100644
--- a/Modules/_testmultiphase.c
+++ b/Modules/_testmultiphase.c
@@ -122,6 +122,8 @@ static PyType_Spec Example_Type_spec = {
 
 
 static PyModuleDef def_meth_state_access;
+static PyModuleDef def_nonmodule;
+static PyModuleDef def_nonmodule_with_methods;
 
 /*[clinic input]
 _testmultiphase.StateAccessType.get_defining_module
@@ -149,6 +151,24 @@ _testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *
     return retval;
 }
 
+/*[clinic input]
+_testmultiphase.StateAccessType.getmodulebydef_bad_def
+
+    cls: defining_class
+
+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]*/
+{
+    _PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule);  // should raise
+    assert(PyErr_Occurred());
+    return NULL;
+}
+
 /*[clinic input]
 _testmultiphase.StateAccessType.increment_count_clinic
 
@@ -245,6 +265,7 @@ _testmultiphase_StateAccessType_get_count_impl(StateAccessTypeObject *self,
 
 static PyMethodDef StateAccessType_methods[] = {
     _TESTMULTIPHASE_STATEACCESSTYPE_GET_DEFINING_MODULE_METHODDEF
+    _TESTMULTIPHASE_STATEACCESSTYPE_GETMODULEBYDEF_BAD_DEF_METHODDEF
     _TESTMULTIPHASE_STATEACCESSTYPE_GET_COUNT_METHODDEF
     _TESTMULTIPHASE_STATEACCESSTYPE_INCREMENT_COUNT_CLINIC_METHODDEF
     {
@@ -433,9 +454,6 @@ PyInit__testmultiphase(PyObject *spec)
 
 /**** Importing a non-module object ****/
 
-static PyModuleDef def_nonmodule;
-static PyModuleDef def_nonmodule_with_methods;
-
 /* Create a SimpleNamespace(three=3) */
 static PyObject*
 createfunc_nonmodule(PyObject *spec, PyModuleDef *def)
diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h
index 55f934be8c6c1..17c28d54ce575 100644
--- a/Modules/clinic/_testmultiphase.c.h
+++ b/Modules/clinic/_testmultiphase.c.h
@@ -35,6 +35,36 @@ _testmultiphase_StateAccessType_get_defining_module(StateAccessTypeObject *self,
     return return_value;
 }
 
+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.");
+
+#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__},
+
+static PyObject *
+_testmultiphase_StateAccessType_getmodulebydef_bad_def_impl(StateAccessTypeObject *self,
+                                                            PyTypeObject *cls);
+
+static PyObject *
+_testmultiphase_StateAccessType_getmodulebydef_bad_def(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    static const char * const _keywords[] = { NULL};
+    static _PyArg_Parser _parser = {":getmodulebydef_bad_def", _keywords, 0};
+
+    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser
+        )) {
+        goto exit;
+    }
+    return_value = _testmultiphase_StateAccessType_getmodulebydef_bad_def_impl(self, cls);
+
+exit:
+    return return_value;
+}
+
 PyDoc_STRVAR(_testmultiphase_StateAccessType_increment_count_clinic__doc__,
 "increment_count_clinic($self, /, n=1, *, twice=False)\n"
 "--\n"
@@ -101,4 +131,4 @@ _testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObj
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=f01137bb3b373e14 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=eb1b8c2ee6290be3 input=a9049054013a1b77]*/
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 10b69fe1196db..deffeb2ccc25d 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -3707,22 +3707,20 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
     // to check i < PyTuple_GET_SIZE(mro) at the first loop iteration.
     assert(PyTuple_GET_SIZE(mro) >= 1);
 
-    Py_ssize_t i = 0;
-    do {
+    Py_ssize_t n = PyTuple_GET_SIZE(mro);
+    for (Py_ssize_t i = 0; i < n; i++) {
         PyObject *super = PyTuple_GET_ITEM(mro, i);
-        // _PyType_GetModuleByDef() must only be called on a heap type created
-        // by PyType_FromModuleAndSpec() or on its subclasses.
-        // type_ready_mro() ensures that a static type cannot inherit from a
-        // heap type.
-        assert(_PyType_HasFeature((PyTypeObject *)type, Py_TPFLAGS_HEAPTYPE));
+        if(!_PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) {
+            // Static types in the MRO need to be skipped
+            continue;
+        }
 
         PyHeapTypeObject *ht = (PyHeapTypeObject*)super;
         PyObject *module = ht->ht_module;
         if (module && _PyModule_GetDef(module) == def) {
             return module;
         }
-        i++;
-    } while (i < PyTuple_GET_SIZE(mro));
+    }
 
     PyErr_Format(
         PyExc_TypeError,



More information about the Python-checkins mailing list