[Python-checkins] bpo-46417: Add _PyType_GetSubclasses() function (GH-30761)

vstinner webhook-mailer at python.org
Fri Jan 21 17:29:21 EST 2022


https://github.com/python/cpython/commit/8ee07dda139f3fa1d7c58a29532a98efc790568d
commit: 8ee07dda139f3fa1d7c58a29532a98efc790568d
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2022-01-21T23:29:10+01:00
summary:

bpo-46417: Add _PyType_GetSubclasses() function (GH-30761)

Add a new _PyType_GetSubclasses() function to get type's subclasses.

_PyType_GetSubclasses(type) returns a list which holds strong
refererences to subclasses. It is safer than iterating on
type->tp_subclasses which yields weak references and can be modified
in the loop.

_PyType_GetSubclasses(type) now holds a reference to the tp_subclasses
dict while creating the list of subclasses.

set_collection_flag_recursive() of _abc.c now uses
_PyType_GetSubclasses().

files:
M Include/internal/pycore_object.h
M Modules/_abc.c
M Objects/typeobject.c

diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 0348563218072..be308cd25d710 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -220,11 +220,12 @@ static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj)
     return ((PyObject **)obj)-3;
 }
 
-PyObject ** _PyObject_DictPointer(PyObject *);
-int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg);
-void _PyObject_ClearInstanceAttributes(PyObject *self);
-void _PyObject_FreeInstanceAttributes(PyObject *self);
-int _PyObject_IsInstanceDictEmpty(PyObject *);
+extern PyObject ** _PyObject_DictPointer(PyObject *);
+extern int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg);
+extern void _PyObject_ClearInstanceAttributes(PyObject *self);
+extern void _PyObject_FreeInstanceAttributes(PyObject *self);
+extern int _PyObject_IsInstanceDictEmpty(PyObject *);
+extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
 
 #ifdef __cplusplus
 }
diff --git a/Modules/_abc.c b/Modules/_abc.c
index b7465c379dddf..a043961812041 100644
--- a/Modules/_abc.c
+++ b/Modules/_abc.c
@@ -4,6 +4,7 @@
 #endif
 
 #include "Python.h"
+#include "pycore_object.h"        // _PyType_GetSubclasses()
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 #include "clinic/_abc.c.h"
 
@@ -493,21 +494,20 @@ set_collection_flag_recursive(PyTypeObject *child, unsigned long flag)
     {
         return;
     }
+
     child->tp_flags &= ~COLLECTION_FLAGS;
     child->tp_flags |= flag;
-    PyObject *grandchildren = child->tp_subclasses;
+
+    PyObject *grandchildren = _PyType_GetSubclasses(child);
     if (grandchildren == NULL) {
         return;
     }
-    assert(PyDict_CheckExact(grandchildren));
-    Py_ssize_t i = 0;
-    while (PyDict_Next(grandchildren, &i, NULL, &grandchildren)) {
-        assert(PyWeakref_CheckRef(grandchildren));
-        PyObject *grandchild = PyWeakref_GET_OBJECT(grandchildren);
-        if (PyType_Check(grandchild)) {
-            set_collection_flag_recursive((PyTypeObject *)grandchild, flag);
-        }
+
+    for (Py_ssize_t i = 0; i < PyList_GET_SIZE(grandchildren); i++) {
+        PyObject *grandchild = PyList_GET_ITEM(grandchildren, i);
+        set_collection_flag_recursive((PyTypeObject *)grandchild, flag);
     }
+    Py_DECREF(grandchildren);
 }
 
 /*[clinic input]
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index c46c3d80edbaa..e4a4824fa2e41 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -687,27 +687,28 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
 static int
 mro_hierarchy(PyTypeObject *type, PyObject *temp)
 {
-    int res;
-    PyObject *new_mro, *old_mro;
-    PyObject *tuple;
-    PyObject *subclasses;
-    Py_ssize_t i, n;
-
-    res = mro_internal(type, &old_mro);
-    if (res <= 0)
+    PyObject *old_mro;
+    int res = mro_internal(type, &old_mro);
+    if (res <= 0) {
         /* error / reentrance */
         return res;
-    new_mro = type->tp_mro;
+    }
+    PyObject *new_mro = type->tp_mro;
 
-    if (old_mro != NULL)
+    PyObject *tuple;
+    if (old_mro != NULL) {
         tuple = PyTuple_Pack(3, type, new_mro, old_mro);
-    else
+    }
+    else {
         tuple = PyTuple_Pack(2, type, new_mro);
+    }
 
-    if (tuple != NULL)
+    if (tuple != NULL) {
         res = PyList_Append(temp, tuple);
-    else
+    }
+    else {
         res = -1;
+    }
     Py_XDECREF(tuple);
 
     if (res < 0) {
@@ -727,15 +728,18 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
        Finally, this makes things simple avoiding the need to deal
        with dictionary iterators and weak references.
     */
-    subclasses = type___subclasses___impl(type);
-    if (subclasses == NULL)
+    PyObject *subclasses = _PyType_GetSubclasses(type);
+    if (subclasses == NULL) {
         return -1;
-    n = PyList_GET_SIZE(subclasses);
-    for (i = 0; i < n; i++) {
+    }
+
+    Py_ssize_t n = PyList_GET_SIZE(subclasses);
+    for (Py_ssize_t i = 0; i < n; i++) {
         PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
         res = mro_hierarchy(subclass, temp);
-        if (res < 0)
+        if (res < 0) {
             break;
+        }
     }
     Py_DECREF(subclasses);
 
@@ -4124,6 +4128,42 @@ type_dealloc(PyTypeObject *type)
     Py_TYPE(type)->tp_free((PyObject *)type);
 }
 
+
+PyObject*
+_PyType_GetSubclasses(PyTypeObject *self)
+{
+    PyObject *list = PyList_New(0);
+    if (list == NULL) {
+        return NULL;
+    }
+
+    // Hold a strong reference to tp_subclasses while iterating on it
+    PyObject *dict = Py_XNewRef(self->tp_subclasses);
+    if (dict == NULL) {
+        return list;
+    }
+    assert(PyDict_CheckExact(dict));
+
+    Py_ssize_t i = 0;
+    PyObject *ref;  // borrowed ref
+    while (PyDict_Next(dict, &i, NULL, &ref)) {
+        assert(PyWeakref_CheckRef(ref));
+        PyObject *obj = PyWeakref_GET_OBJECT(ref);  // borrowed ref
+        if (obj == Py_None) {
+            continue;
+        }
+        assert(PyType_Check(obj));
+        if (PyList_Append(list, obj) < 0) {
+            Py_CLEAR(list);
+            goto done;
+        }
+    }
+done:
+    Py_DECREF(dict);
+    return list;
+}
+
+
 /*[clinic input]
 type.__subclasses__
 
@@ -4134,28 +4174,7 @@ static PyObject *
 type___subclasses___impl(PyTypeObject *self)
 /*[clinic end generated code: output=eb5eb54485942819 input=5af66132436f9a7b]*/
 {
-    PyObject *list, *raw, *ref;
-    Py_ssize_t i;
-
-    list = PyList_New(0);
-    if (list == NULL)
-        return NULL;
-    raw = self->tp_subclasses;
-    if (raw == NULL)
-        return list;
-    assert(PyDict_CheckExact(raw));
-    i = 0;
-    while (PyDict_Next(raw, &i, NULL, &ref)) {
-        assert(PyWeakref_CheckRef(ref));
-        ref = PyWeakref_GET_OBJECT(ref);
-        if (ref != Py_None) {
-            if (PyList_Append(list, ref) < 0) {
-                Py_DECREF(list);
-                return NULL;
-            }
-        }
-    }
-    return list;
+    return _PyType_GetSubclasses(self);
 }
 
 static PyObject *
@@ -4165,6 +4184,7 @@ type_prepare(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
     return PyDict_New();
 }
 
+
 /*
    Merge the __dict__ of aclass into dict, and recursively also all
    the __dict__s of aclass's base classes.  The order of merging isn't



More information about the Python-checkins mailing list