diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst new file mode 100644 index 00000000000..69017293649 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst @@ -0,0 +1,2 @@ +Avoid caching attributes of classes which type defines mro() to avoid a hard +cache invalidation problem. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 339f7285292..fc809d36e10 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -78,6 +78,9 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static void clear_slotdefs(void); +static PyObject * +lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound); + /* * finds the beginning of the docstring's introspection signature. * if present, returns a pointer pointing to the first '('. @@ -287,17 +290,35 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type has a custom MRO that includes a type which is not officially - super type. + super type, or if the type implements its own mro() method. Called from mro_internal, which will subsequently be called on each subclass when their mro is recursively updated. */ Py_ssize_t i, n; - int clear = 0; + int custom = (Py_TYPE(type) != &PyType_Type); + int unbound; + PyObject *mro_meth = NULL; + PyObject *type_mro_meth = NULL; if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) return; + if (custom) { + _Py_IDENTIFIER(mro); + mro_meth = lookup_maybe_method( + (PyObject *)type, &PyId_mro, &unbound); + if (mro_meth == NULL) + goto clear; + type_mro_meth = lookup_maybe_method( + (PyObject *)&PyType_Type, &PyId_mro, &unbound); + if (type_mro_meth == NULL) + goto clear; + if (mro_meth != type_mro_meth) + goto clear; + Py_XDECREF(mro_meth); + Py_XDECREF(type_mro_meth); + } n = PyTuple_GET_SIZE(bases); for (i = 0; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(bases, i); @@ -308,14 +329,15 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || !PyType_IsSubtype(type, cls)) { - clear = 1; - break; + goto clear; } } - - if (clear) - type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| - Py_TPFLAGS_VALID_VERSION_TAG); + return; + clear: + Py_XDECREF(mro_meth); + Py_XDECREF(type_mro_meth); + type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| + Py_TPFLAGS_VALID_VERSION_TAG); } static int