mirror of https://github.com/python/cpython
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().
This commit is contained in:
parent
57d1855682
commit
8ee07dda13
|
@ -220,11 +220,12 @@ static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj)
|
||||||
return ((PyObject **)obj)-3;
|
return ((PyObject **)obj)-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject ** _PyObject_DictPointer(PyObject *);
|
extern PyObject ** _PyObject_DictPointer(PyObject *);
|
||||||
int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg);
|
extern int _PyObject_VisitInstanceAttributes(PyObject *self, visitproc visit, void *arg);
|
||||||
void _PyObject_ClearInstanceAttributes(PyObject *self);
|
extern void _PyObject_ClearInstanceAttributes(PyObject *self);
|
||||||
void _PyObject_FreeInstanceAttributes(PyObject *self);
|
extern void _PyObject_FreeInstanceAttributes(PyObject *self);
|
||||||
int _PyObject_IsInstanceDictEmpty(PyObject *);
|
extern int _PyObject_IsInstanceDictEmpty(PyObject *);
|
||||||
|
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "pycore_object.h" // _PyType_GetSubclasses()
|
||||||
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
||||||
#include "clinic/_abc.c.h"
|
#include "clinic/_abc.c.h"
|
||||||
|
|
||||||
|
@ -493,21 +494,20 @@ set_collection_flag_recursive(PyTypeObject *child, unsigned long flag)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
child->tp_flags &= ~COLLECTION_FLAGS;
|
child->tp_flags &= ~COLLECTION_FLAGS;
|
||||||
child->tp_flags |= flag;
|
child->tp_flags |= flag;
|
||||||
PyObject *grandchildren = child->tp_subclasses;
|
|
||||||
|
PyObject *grandchildren = _PyType_GetSubclasses(child);
|
||||||
if (grandchildren == NULL) {
|
if (grandchildren == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(PyDict_CheckExact(grandchildren));
|
|
||||||
Py_ssize_t i = 0;
|
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(grandchildren); i++) {
|
||||||
while (PyDict_Next(grandchildren, &i, NULL, &grandchildren)) {
|
PyObject *grandchild = PyList_GET_ITEM(grandchildren, i);
|
||||||
assert(PyWeakref_CheckRef(grandchildren));
|
|
||||||
PyObject *grandchild = PyWeakref_GET_OBJECT(grandchildren);
|
|
||||||
if (PyType_Check(grandchild)) {
|
|
||||||
set_collection_flag_recursive((PyTypeObject *)grandchild, flag);
|
set_collection_flag_recursive((PyTypeObject *)grandchild, flag);
|
||||||
}
|
}
|
||||||
}
|
Py_DECREF(grandchildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
|
|
@ -687,27 +687,28 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
static int
|
static int
|
||||||
mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
||||||
{
|
{
|
||||||
int res;
|
PyObject *old_mro;
|
||||||
PyObject *new_mro, *old_mro;
|
int res = mro_internal(type, &old_mro);
|
||||||
PyObject *tuple;
|
if (res <= 0) {
|
||||||
PyObject *subclasses;
|
|
||||||
Py_ssize_t i, n;
|
|
||||||
|
|
||||||
res = mro_internal(type, &old_mro);
|
|
||||||
if (res <= 0)
|
|
||||||
/* error / reentrance */
|
/* error / reentrance */
|
||||||
return res;
|
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);
|
tuple = PyTuple_Pack(3, type, new_mro, old_mro);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
tuple = PyTuple_Pack(2, type, new_mro);
|
tuple = PyTuple_Pack(2, type, new_mro);
|
||||||
|
}
|
||||||
|
|
||||||
if (tuple != NULL)
|
if (tuple != NULL) {
|
||||||
res = PyList_Append(temp, tuple);
|
res = PyList_Append(temp, tuple);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
res = -1;
|
res = -1;
|
||||||
|
}
|
||||||
Py_XDECREF(tuple);
|
Py_XDECREF(tuple);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -727,16 +728,19 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
||||||
Finally, this makes things simple avoiding the need to deal
|
Finally, this makes things simple avoiding the need to deal
|
||||||
with dictionary iterators and weak references.
|
with dictionary iterators and weak references.
|
||||||
*/
|
*/
|
||||||
subclasses = type___subclasses___impl(type);
|
PyObject *subclasses = _PyType_GetSubclasses(type);
|
||||||
if (subclasses == NULL)
|
if (subclasses == NULL) {
|
||||||
return -1;
|
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));
|
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
|
||||||
res = mro_hierarchy(subclass, temp);
|
res = mro_hierarchy(subclass, temp);
|
||||||
if (res < 0)
|
if (res < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Py_DECREF(subclasses);
|
Py_DECREF(subclasses);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -4124,6 +4128,42 @@ type_dealloc(PyTypeObject *type)
|
||||||
Py_TYPE(type)->tp_free((PyObject *)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]
|
/*[clinic input]
|
||||||
type.__subclasses__
|
type.__subclasses__
|
||||||
|
|
||||||
|
@ -4134,28 +4174,7 @@ static PyObject *
|
||||||
type___subclasses___impl(PyTypeObject *self)
|
type___subclasses___impl(PyTypeObject *self)
|
||||||
/*[clinic end generated code: output=eb5eb54485942819 input=5af66132436f9a7b]*/
|
/*[clinic end generated code: output=eb5eb54485942819 input=5af66132436f9a7b]*/
|
||||||
{
|
{
|
||||||
PyObject *list, *raw, *ref;
|
return _PyType_GetSubclasses(self);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -4165,6 +4184,7 @@ type_prepare(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
|
||||||
return PyDict_New();
|
return PyDict_New();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Merge the __dict__ of aclass into dict, and recursively also all
|
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
|
the __dict__s of aclass's base classes. The order of merging isn't
|
||||||
|
|
Loading…
Reference in New Issue