Skip to content

Commit 180dc1b

Browse files
JulienPalardmarkshannon
authored andcommitted
bpo-28866: No type cache for types with specialized mro, invalidation is hard. (#13157)
* No type cache for types with specialized mro, invalidation is hard. * FIX: Don't disable method cache custom types that do not implement mro(). * fixing implem. * Avoid storing error flags, also decref. * news entry * Clear as soon as we're getting an error. * FIX: Reference leak.
1 parent 135c6a5 commit 180dc1b

File tree

2 files changed

+32
-8
lines changed

2 files changed

+32
-8
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid caching attributes of classes which type defines mro() to avoid a hard
2+
cache invalidation problem.

Objects/typeobject.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
7878
static void
7979
clear_slotdefs(void);
8080

81+
static PyObject *
82+
lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound);
83+
8184
/*
8285
* finds the beginning of the docstring's introspection signature.
8386
* if present, returns a pointer pointing to the first '('.
@@ -287,17 +290,35 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
287290
288291
Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type
289292
has a custom MRO that includes a type which is not officially
290-
super type.
293+
super type, or if the type implements its own mro() method.
291294
292295
Called from mro_internal, which will subsequently be called on
293296
each subclass when their mro is recursively updated.
294297
*/
295298
Py_ssize_t i, n;
296-
int clear = 0;
299+
int custom = (Py_TYPE(type) != &PyType_Type);
300+
int unbound;
301+
PyObject *mro_meth = NULL;
302+
PyObject *type_mro_meth = NULL;
297303

298304
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
299305
return;
300306

307+
if (custom) {
308+
_Py_IDENTIFIER(mro);
309+
mro_meth = lookup_maybe_method(
310+
(PyObject *)type, &PyId_mro, &unbound);
311+
if (mro_meth == NULL)
312+
goto clear;
313+
type_mro_meth = lookup_maybe_method(
314+
(PyObject *)&PyType_Type, &PyId_mro, &unbound);
315+
if (type_mro_meth == NULL)
316+
goto clear;
317+
if (mro_meth != type_mro_meth)
318+
goto clear;
319+
Py_XDECREF(mro_meth);
320+
Py_XDECREF(type_mro_meth);
321+
}
301322
n = PyTuple_GET_SIZE(bases);
302323
for (i = 0; i < n; i++) {
303324
PyObject *b = PyTuple_GET_ITEM(bases, i);
@@ -308,14 +329,15 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
308329

309330
if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
310331
!PyType_IsSubtype(type, cls)) {
311-
clear = 1;
312-
break;
332+
goto clear;
313333
}
314334
}
315-
316-
if (clear)
317-
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
318-
Py_TPFLAGS_VALID_VERSION_TAG);
335+
return;
336+
clear:
337+
Py_XDECREF(mro_meth);
338+
Py_XDECREF(type_mro_meth);
339+
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
340+
Py_TPFLAGS_VALID_VERSION_TAG);
319341
}
320342

321343
static int

0 commit comments

Comments
 (0)