[Python-checkins] bpo-46417: Cleanup typeobject.c code (GH-30795)
vstinner
webhook-mailer at python.org
Sat Jan 22 12:56:15 EST 2022
https://github.com/python/cpython/commit/3a4c15bb9815b6f4652621fe6043ae18e0d202b3
commit: 3a4c15bb9815b6f4652621fe6043ae18e0d202b3
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2022-01-22T18:56:11+01:00
summary:
bpo-46417: Cleanup typeobject.c code (GH-30795)
* Add comment to recurse_down_subclasses() explaining why it's safe
to use a borrowed reference to tp_subclasses.
* remove_all_subclasses() no longer accept NULL cases
* type_set_bases() now relies on the fact that new_bases is not NULL.
* type_dealloc_common() avoids PyErr_Fetch/PyErr_Restore if tp_bases
is NULL.
* remove_all_subclasses() makes sure that no exception is raised.
* Don't test at runtime if tp_mro only contains types: rely on
_PyType_CAST() assertion for that.
* _PyStaticType_Dealloc() no longer clears tp_subclasses which is
already NULL.
* mro_hierarchy() avoids calling _PyType_GetSubclasses() if
tp_subclasses is NULL.
Coding style:
* Use Py_NewRef().
* Add braces and move variable declarations to the first variable
assignement.
* Rename a few variables and parameters to use better names.
files:
M Objects/typeobject.c
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index b3c305e0bf430..bf62b5389257f 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -328,24 +328,26 @@ PyType_Modified(PyTypeObject *type)
We don't assign new version tags eagerly, but only as
needed.
*/
- PyObject *raw, *ref;
- Py_ssize_t i;
-
- if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+ if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
return;
+ }
+
+ PyObject *subclasses = type->tp_subclasses;
+ if (subclasses != NULL) {
+ assert(PyDict_CheckExact(subclasses));
- raw = type->tp_subclasses;
- if (raw != NULL) {
- assert(PyDict_CheckExact(raw));
- i = 0;
- while (PyDict_Next(raw, &i, NULL, &ref)) {
+ Py_ssize_t i = 0;
+ PyObject *ref;
+ while (PyDict_Next(subclasses, &i, NULL, &ref)) {
assert(PyWeakref_CheckRef(ref));
- ref = PyWeakref_GET_OBJECT(ref);
- if (ref != Py_None) {
- PyType_Modified(_PyType_CAST(ref));
+ PyObject *obj = PyWeakref_GET_OBJECT(ref);
+ if (obj == Py_None) {
+ continue;
}
+ PyType_Modified(_PyType_CAST(obj));
}
}
+
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
type->tp_version_tag = 0; /* 0 is not a valid version tag */
}
@@ -409,13 +411,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
must first be done on all super classes. Return 0 if this
cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
*/
- Py_ssize_t i, n;
- PyObject *bases;
-
- if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+ if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
return 1;
- if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
+ }
+ if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
return 0;
+ }
if (next_version_tag == 0) {
/* We have run out of version numbers */
@@ -424,9 +425,9 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
type->tp_version_tag = next_version_tag++;
assert (type->tp_version_tag != 0);
- bases = type->tp_bases;
- n = PyTuple_GET_SIZE(bases);
- for (i = 0; i < n; i++) {
+ PyObject *bases = type->tp_bases;
+ Py_ssize_t n = PyTuple_GET_SIZE(bases);
+ for (Py_ssize_t i = 0; i < n; i++) {
PyObject *b = PyTuple_GET_ITEM(bases, i);
if (!assign_version_tag(cache, _PyType_CAST(b)))
return 0;
@@ -679,7 +680,7 @@ static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
static void update_all_slots(PyTypeObject *);
typedef int (*update_callback)(PyTypeObject *, void *);
-static int update_subclasses(PyTypeObject *type, PyObject *name,
+static int update_subclasses(PyTypeObject *type, PyObject *attr_name,
update_callback callback, void *data);
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
update_callback callback, void *data);
@@ -718,30 +719,33 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
}
Py_XDECREF(old_mro);
- /* Obtain a copy of subclasses list to iterate over.
+ // Avoid creating an empty list if there is no subclass
+ if (type->tp_subclasses != NULL) {
+ /* Obtain a copy of subclasses list to iterate over.
- Otherwise type->tp_subclasses might be altered
- in the middle of the loop, for example, through a custom mro(),
- by invoking type_set_bases on some subclass of the type
- which in turn calls remove_subclass/add_subclass on this type.
+ Otherwise type->tp_subclasses might be altered
+ in the middle of the loop, for example, through a custom mro(),
+ by invoking type_set_bases on some subclass of the type
+ which in turn calls remove_subclass/add_subclass on this type.
- Finally, this makes things simple avoiding the need to deal
- with dictionary iterators and weak references.
- */
- PyObject *subclasses = _PyType_GetSubclasses(type);
- if (subclasses == NULL) {
- return -1;
- }
+ Finally, this makes things simple avoiding the need to deal
+ with dictionary iterators and weak references.
+ */
+ PyObject *subclasses = _PyType_GetSubclasses(type);
+ if (subclasses == NULL) {
+ return -1;
+ }
- 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) {
- break;
+ 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) {
+ break;
+ }
}
+ Py_DECREF(subclasses);
}
- Py_DECREF(subclasses);
return res;
}
@@ -749,14 +753,12 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
static int
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
{
- int res = 0;
- PyObject *temp;
- PyObject *old_bases;
- PyTypeObject *new_base, *old_base;
- Py_ssize_t i;
-
- if (!check_set_special_type_attr(type, new_bases, "__bases__"))
+ // Check arguments
+ if (!check_set_special_type_attr(type, new_bases, "__bases__")) {
return -1;
+ }
+ assert(new_bases != NULL);
+
if (!PyTuple_Check(new_bases)) {
PyErr_Format(PyExc_TypeError,
"can only assign tuple to %s.__bases__, not %s",
@@ -769,7 +771,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
type->tp_name);
return -1;
}
- for (i = 0; i < PyTuple_GET_SIZE(new_bases); i++) {
+ Py_ssize_t n = PyTuple_GET_SIZE(new_bases);
+ for (Py_ssize_t i = 0; i < n; i++) {
PyObject *ob = PyTuple_GET_ITEM(new_bases, i);
if (!PyType_Check(ob)) {
PyErr_Format(PyExc_TypeError,
@@ -789,39 +792,42 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
below), which in turn may cause an inheritance cycle
through tp_base chain. And this is definitely
not what you want to ever happen. */
- (base->tp_mro != NULL && type_is_subtype_base_chain(base, type))) {
-
+ (base->tp_mro != NULL && type_is_subtype_base_chain(base, type)))
+ {
PyErr_SetString(PyExc_TypeError,
"a __bases__ item causes an inheritance cycle");
return -1;
}
}
- new_base = best_base(new_bases);
+ // Compute the new MRO and the new base class
+ PyTypeObject *new_base = best_base(new_bases);
if (new_base == NULL)
return -1;
- if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
+ if (!compatible_for_assignment(type->tp_base, new_base, "__bases__")) {
return -1;
+ }
- Py_INCREF(new_bases);
- Py_INCREF(new_base);
-
- old_bases = type->tp_bases;
- old_base = type->tp_base;
+ PyObject *old_bases = type->tp_bases;
+ assert(old_bases != NULL);
+ PyTypeObject *old_base = type->tp_base;
- type->tp_bases = new_bases;
- type->tp_base = new_base;
+ type->tp_bases = Py_NewRef(new_bases);
+ type->tp_base = (PyTypeObject *)Py_NewRef(new_base);
- temp = PyList_New(0);
- if (temp == NULL)
+ PyObject *temp = PyList_New(0);
+ if (temp == NULL) {
goto bail;
- if (mro_hierarchy(type, temp) < 0)
+ }
+ if (mro_hierarchy(type, temp) < 0) {
goto undo;
+ }
Py_DECREF(temp);
/* Take no action in case if type->tp_bases has been replaced
through reentrance. */
+ int res;
if (type->tp_bases == new_bases) {
/* any base that was in __bases__ but now isn't, we
need to remove |type| from its tp_subclasses.
@@ -834,6 +840,9 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
res = add_all_subclasses(type, new_bases);
update_all_slots(type);
}
+ else {
+ res = 0;
+ }
Py_DECREF(old_bases);
Py_DECREF(old_base);
@@ -842,7 +851,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
return res;
undo:
- for (i = PyList_GET_SIZE(temp) - 1; i >= 0; i--) {
+ n = PyList_GET_SIZE(temp);
+ for (Py_ssize_t i = n - 1; i >= 0; i--) {
PyTypeObject *cls;
PyObject *new_mro, *old_mro = NULL;
@@ -1413,8 +1423,9 @@ subtype_dealloc(PyObject *self)
and if self is tracked at that point, it will look like trash to GC and GC
will try to delete self again.
*/
- if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
+ if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
PyObject_ClearWeakRefs(self);
+ }
if (type->tp_del) {
_PyObject_GC_TRACK(self);
@@ -1929,20 +1940,14 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
static PyObject *
mro_implementation(PyTypeObject *type)
{
- PyObject *result;
- PyObject *bases;
- PyObject **to_merge;
- Py_ssize_t i, n;
-
if (!_PyType_IsReady(type)) {
if (PyType_Ready(type) < 0)
return NULL;
}
- bases = type->tp_bases;
- assert(PyTuple_Check(bases));
- n = PyTuple_GET_SIZE(bases);
- for (i = 0; i < n; i++) {
+ PyObject *bases = type->tp_bases;
+ Py_ssize_t n = PyTuple_GET_SIZE(bases);
+ for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
if (base->tp_mro == NULL) {
PyErr_Format(PyExc_TypeError,
@@ -1959,13 +1964,14 @@ mro_implementation(PyTypeObject *type)
*/
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
- result = PyTuple_New(k + 1);
+ PyObject *result = PyTuple_New(k + 1);
if (result == NULL) {
return NULL;
}
+
Py_INCREF(type);
PyTuple_SET_ITEM(result, 0, (PyObject *) type);
- for (i = 0; i < k; i++) {
+ for (Py_ssize_t i = 0; i < k; i++) {
PyObject *cls = PyTuple_GET_ITEM(base->tp_mro, i);
Py_INCREF(cls);
PyTuple_SET_ITEM(result, i + 1, cls);
@@ -1986,20 +1992,19 @@ mro_implementation(PyTypeObject *type)
linearization implied by a base class. The last element of
to_merge is the declared tuple of bases.
*/
-
- to_merge = PyMem_New(PyObject *, n + 1);
+ PyObject **to_merge = PyMem_New(PyObject *, n + 1);
if (to_merge == NULL) {
PyErr_NoMemory();
return NULL;
}
- for (i = 0; i < n; i++) {
+ for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
to_merge[i] = base->tp_mro;
}
to_merge[n] = bases;
- result = PyList_New(1);
+ PyObject *result = PyList_New(1);
if (result == NULL) {
PyMem_Free(to_merge);
return NULL;
@@ -2010,8 +2015,8 @@ mro_implementation(PyTypeObject *type)
if (pmerge(result, to_merge, n + 1) < 0) {
Py_CLEAR(result);
}
-
PyMem_Free(to_merge);
+
return result;
}
@@ -2651,20 +2656,20 @@ type_new_slots_bases(type_new_ctx *ctx)
(ctx->may_add_weak && ctx->add_weak == 0)))
{
for (Py_ssize_t i = 0; i < nbases; i++) {
- PyObject *base = PyTuple_GET_ITEM(ctx->bases, i);
- if (base == (PyObject *)ctx->base) {
+ PyObject *obj = PyTuple_GET_ITEM(ctx->bases, i);
+ if (obj == (PyObject *)ctx->base) {
/* Skip primary base */
continue;
}
- PyTypeObject *type = _PyType_CAST(base);
+ PyTypeObject *base = _PyType_CAST(obj);
if (ctx->may_add_dict && ctx->add_dict == 0 &&
- type->tp_dictoffset != 0)
+ base->tp_dictoffset != 0)
{
ctx->add_dict++;
}
if (ctx->may_add_weak && ctx->add_weak == 0 &&
- type->tp_weaklistoffset != 0)
+ base->tp_weaklistoffset != 0)
{
ctx->add_weak++;
}
@@ -3739,8 +3744,8 @@ _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.
@@ -3753,8 +3758,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
if (module && _PyModule_GetDef(module) == def) {
return module;
}
- i++;
- } while (i < PyTuple_GET_SIZE(mro));
+ }
PyErr_Format(
PyExc_TypeError,
@@ -3770,10 +3774,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
static PyObject *
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
{
- Py_ssize_t i, n;
- PyObject *mro, *res, *base, *dict;
Py_hash_t hash;
-
if (!PyUnicode_CheckExact(name) ||
(hash = ((PyASCIIObject *) name)->hash) == -1)
{
@@ -3785,8 +3786,7 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
}
/* Look in tp_dict of types in MRO */
- mro = type->tp_mro;
-
+ PyObject *mro = type->tp_mro;
if (mro == NULL) {
if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
if (PyType_Ready(type) < 0) {
@@ -3801,20 +3801,19 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
}
}
- res = NULL;
+ PyObject *res = NULL;
/* Keep a strong reference to mro because type->tp_mro can be replaced
during dict lookup, e.g. when comparing to non-string keys. */
Py_INCREF(mro);
- assert(PyTuple_Check(mro));
- n = PyTuple_GET_SIZE(mro);
- for (i = 0; i < n; i++) {
- base = PyTuple_GET_ITEM(mro, i);
- assert(PyType_Check(base));
- dict = _PyType_CAST(base)->tp_dict;
+ Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyObject *base = PyTuple_GET_ITEM(mro, i);
+ PyObject *dict = _PyType_CAST(base)->tp_dict;
assert(dict && PyDict_Check(dict));
res = _PyDict_GetItem_KnownHash(dict, name, hash);
- if (res != NULL)
+ if (res != NULL) {
break;
+ }
if (PyErr_Occurred()) {
*error = -1;
goto done;
@@ -4066,10 +4065,12 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys);
static void
type_dealloc_common(PyTypeObject *type)
{
- PyObject *tp, *val, *tb;
- PyErr_Fetch(&tp, &val, &tb);
- remove_all_subclasses(type, type->tp_bases);
- PyErr_Restore(tp, val, tb);
+ if (type->tp_bases != NULL) {
+ PyObject *tp, *val, *tb;
+ PyErr_Fetch(&tp, &val, &tb);
+ remove_all_subclasses(type, type->tp_bases);
+ PyErr_Restore(tp, val, tb);
+ }
PyObject_ClearWeakRefs((PyObject *)type);
}
@@ -4089,7 +4090,7 @@ _PyStaticType_Dealloc(PyTypeObject *type)
Py_CLEAR(type->tp_bases);
Py_CLEAR(type->tp_mro);
Py_CLEAR(type->tp_cache);
- Py_CLEAR(type->tp_subclasses);
+ // type->tp_subclasses is NULL
type->tp_flags &= ~Py_TPFLAGS_READY;
}
@@ -4154,6 +4155,7 @@ _PyType_GetSubclasses(PyTypeObject *self)
continue;
}
assert(PyType_Check(obj));
+
if (PyList_Append(list, obj) < 0) {
Py_DECREF(list);
return NULL;
@@ -6227,7 +6229,7 @@ type_ready_mro(PyTypeObject *type)
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(mro, i));
- if (PyType_Check(base) && (base->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
+ if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) {
PyErr_Format(PyExc_TypeError,
"type '%.100s' is not dynamically allocated but "
"its base type '%.100s' is dynamically allocated",
@@ -6515,15 +6517,15 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
// Only get tp_subclasses after creating the key and value.
// PyWeakref_NewRef() can trigger a garbage collection which can execute
// arbitrary Python code and so modify base->tp_subclasses.
- PyObject *dict = base->tp_subclasses;
- if (dict == NULL) {
- base->tp_subclasses = dict = PyDict_New();
- if (dict == NULL)
+ PyObject *subclasses = base->tp_subclasses;
+ if (subclasses == NULL) {
+ base->tp_subclasses = subclasses = PyDict_New();
+ if (subclasses == NULL)
return -1;
}
- assert(PyDict_CheckExact(dict));
+ assert(PyDict_CheckExact(subclasses));
- int result = PyDict_SetItem(dict, key, ref);
+ int result = PyDict_SetItem(subclasses, key, ref);
Py_DECREF(ref);
Py_DECREF(key);
return result;
@@ -6532,35 +6534,30 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
static int
add_all_subclasses(PyTypeObject *type, PyObject *bases)
{
+ Py_ssize_t n = PyTuple_GET_SIZE(bases);
int res = 0;
-
- if (bases) {
- Py_ssize_t i;
- for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
- PyObject *base = PyTuple_GET_ITEM(bases, i);
- if (PyType_Check(base) &&
- add_subclass((PyTypeObject*)base, type) < 0)
- {
- res = -1;
- }
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyObject *obj = PyTuple_GET_ITEM(bases, i);
+ // bases tuple must only contain types
+ PyTypeObject *base = _PyType_CAST(obj);
+ if (add_subclass(base, type) < 0) {
+ res = -1;
}
}
-
return res;
}
static void
remove_subclass(PyTypeObject *base, PyTypeObject *type)
{
- PyObject *dict, *key;
-
- dict = base->tp_subclasses;
- if (dict == NULL) {
+ PyObject *subclasses = base->tp_subclasses; // borrowed ref
+ if (subclasses == NULL) {
return;
}
- assert(PyDict_CheckExact(dict));
- key = PyLong_FromVoidPtr((void *) type);
- if (key == NULL || PyDict_DelItem(dict, key)) {
+ assert(PyDict_CheckExact(subclasses));
+
+ PyObject *key = PyLong_FromVoidPtr((void *) type);
+ if (key == NULL || PyDict_DelItem(subclasses, key)) {
/* This can happen if the type initialization errored out before
the base subclasses were updated (e.g. a non-str __qualname__
was passed in the type dict). */
@@ -6568,7 +6565,7 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
}
Py_XDECREF(key);
- if (PyDict_Size(dict) == 0) {
+ if (PyDict_Size(subclasses) == 0) {
// Delete the dictionary to save memory. _PyStaticType_Dealloc()
// callers also test if tp_subclasses is NULL to check if a static type
// has no subclass.
@@ -6579,15 +6576,17 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
static void
remove_all_subclasses(PyTypeObject *type, PyObject *bases)
{
- if (bases) {
- Py_ssize_t i;
- for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
- PyObject *base = PyTuple_GET_ITEM(bases, i);
- if (PyType_Check(base)) {
- remove_subclass((PyTypeObject*) base, type);
- }
+ assert(bases != NULL);
+ // remove_subclass() can clear the current exception
+ assert(!PyErr_Occurred());
+
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(bases); i++) {
+ PyObject *base = PyTuple_GET_ITEM(bases, i);
+ if (PyType_Check(base)) {
+ remove_subclass((PyTypeObject*) base, type);
}
}
+ assert(!PyErr_Occurred());
}
static int
@@ -8466,9 +8465,9 @@ static int
update_slots_callback(PyTypeObject *type, void *data)
{
slotdef **pp = (slotdef **)data;
-
- for (; *pp; pp++)
+ for (; *pp; pp++) {
update_one_slot(type, *pp);
+ }
return 0;
}
@@ -8654,29 +8653,33 @@ type_new_init_subclass(PyTypeObject *type, PyObject *kwds)
/* recurse_down_subclasses() and update_subclasses() are mutually
recursive functions to call a callback for all subclasses,
- but refraining from recursing into subclasses that define 'name'. */
+ but refraining from recursing into subclasses that define 'attr_name'. */
static int
-update_subclasses(PyTypeObject *type, PyObject *name,
+update_subclasses(PyTypeObject *type, PyObject *attr_name,
update_callback callback, void *data)
{
- if (callback(type, data) < 0)
+ if (callback(type, data) < 0) {
return -1;
- return recurse_down_subclasses(type, name, callback, data);
+ }
+ return recurse_down_subclasses(type, attr_name, callback, data);
}
static int
-recurse_down_subclasses(PyTypeObject *type, PyObject *name,
+recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
update_callback callback, void *data)
{
- PyObject *ref, *subclasses, *dict;
- Py_ssize_t i;
-
- subclasses = type->tp_subclasses;
- if (subclasses == NULL)
+ // It is safe to use a borrowed reference because update_subclasses() is
+ // only used with update_slots_callback() which doesn't modify
+ // tp_subclasses.
+ PyObject *subclasses = type->tp_subclasses; // borrowed ref
+ if (subclasses == NULL) {
return 0;
+ }
assert(PyDict_CheckExact(subclasses));
- i = 0;
+
+ Py_ssize_t i = 0;
+ PyObject *ref;
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
assert(PyWeakref_CheckRef(ref));
PyObject *obj = PyWeakref_GET_OBJECT(ref);
@@ -8687,18 +8690,20 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name,
PyTypeObject *subclass = _PyType_CAST(obj);
/* Avoid recursing down into unaffected classes */
- dict = subclass->tp_dict;
+ PyObject *dict = subclass->tp_dict;
if (dict != NULL && PyDict_Check(dict)) {
- int r = PyDict_Contains(dict, name);
- if (r > 0) {
- continue;
- }
+ int r = PyDict_Contains(dict, attr_name);
if (r < 0) {
return -1;
}
+ if (r > 0) {
+ continue;
+ }
}
- if (update_subclasses(subclass, name, callback, data) < 0)
+
+ if (update_subclasses(subclass, attr_name, callback, data) < 0) {
return -1;
+ }
}
return 0;
}
More information about the Python-checkins
mailing list