Skip to content

Commit bfd0b77

Browse files
[3.7] bpo-28866: No type cache for types with specialized mro, invalidation is hard. (GH-13157) (GH-13589)
* 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. (cherry picked from commit 180dc1b) Co-authored-by: Julien Palard <[email protected]> https://bugs.python.org/issue28866
1 parent f3d9094 commit bfd0b77

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
@@ -77,6 +77,9 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
7777
static void
7878
clear_slotdefs(void);
7979

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

292298
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
293299
return;
294300

301+
if (custom) {
302+
_Py_IDENTIFIER(mro);
303+
mro_meth = lookup_maybe_method(
304+
(PyObject *)type, &PyId_mro, &unbound);
305+
if (mro_meth == NULL)
306+
goto clear;
307+
type_mro_meth = lookup_maybe_method(
308+
(PyObject *)&PyType_Type, &PyId_mro, &unbound);
309+
if (type_mro_meth == NULL)
310+
goto clear;
311+
if (mro_meth != type_mro_meth)
312+
goto clear;
313+
Py_XDECREF(mro_meth);
314+
Py_XDECREF(type_mro_meth);
315+
}
295316
n = PyTuple_GET_SIZE(bases);
296317
for (i = 0; i < n; i++) {
297318
PyObject *b = PyTuple_GET_ITEM(bases, i);
@@ -302,14 +323,15 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
302323

303324
if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
304325
!PyType_IsSubtype(type, cls)) {
305-
clear = 1;
306-
break;
326+
goto clear;
307327
}
308328
}
309-
310-
if (clear)
311-
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
312-
Py_TPFLAGS_VALID_VERSION_TAG);
329+
return;
330+
clear:
331+
Py_XDECREF(mro_meth);
332+
Py_XDECREF(type_mro_meth);
333+
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
334+
Py_TPFLAGS_VALID_VERSION_TAG);
313335
}
314336

315337
static int

0 commit comments

Comments
 (0)