pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/aa6579cb60b4fcd652d7bc83fed2668e4ae84db3

gh-127773: Disable attribute cache on incompatible MRO entries (GH-12… · python/cpython@aa6579c · GitHub
Skip to content

Commit aa6579c

Browse files
authored
gh-127773: Disable attribute cache on incompatible MRO entries (GH-127924)
1 parent 76ffaef commit aa6579c

4 files changed

Lines changed: 50 additions & 2 deletions

File tree

Include/cpython/object.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,27 @@ struct _typeobject {
221221
PyObject *tp_weaklist; /* not used for static builtin types */
222222
destructor tp_del;
223223

224-
/* Type attribute cache version tag. Added in version 2.6 */
224+
/* Type attribute cache version tag. Added in version 2.6.
225+
* If zero, the cache is invalid and must be initialized.
226+
*/
225227
unsigned int tp_version_tag;
226228

227229
destructor tp_finalize;
228230
vectorcallfunc tp_vectorcall;
229231

230232
/* bitset of which type-watchers care about this type */
231233
unsigned char tp_watched;
234+
235+
/* Number of tp_version_tag values used.
236+
* Set to _Py_ATTR_CACHE_UNUSED if the attribute cache is
237+
* disabled for this type (e.g. due to custom MRO entries).
238+
* Otherwise, limited to MAX_VERSIONS_PER_CLASS (defined elsewhere).
239+
*/
232240
uint16_t tp_versions_used;
233241
};
234242

243+
#define _Py_ATTR_CACHE_UNUSED (30000) // (see tp_versions_used)
244+
235245
/* This struct is used by the specializer
236246
* It should be treated as an opaque blob
237247
* by code other than the specializer and interpreter. */

Lib/test/test_metaclass.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,33 @@
254254
[...]
255255
test.test_metaclass.ObscureException
256256
257+
Test setting attributes with a non-base type in mro() (gh-127773).
258+
259+
>>> class Base:
260+
... value = 1
261+
...
262+
>>> class Meta(type):
263+
... def mro(cls):
264+
... return (cls, Base, object)
265+
...
266+
>>> class WeirdClass(metaclass=Meta):
267+
... pass
268+
...
269+
>>> Base.value
270+
1
271+
>>> WeirdClass.value
272+
1
273+
>>> Base.value = 2
274+
>>> Base.value
275+
2
276+
>>> WeirdClass.value
277+
2
278+
>>> Base.value = 3
279+
>>> Base.value
280+
3
281+
>>> WeirdClass.value
282+
3
283+
257284
"""
258285

259286
import sys
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do not use the type attribute cache for types with incompatible :term:`MRO`.

Objects/typeobject.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@ static void
992992
set_version_unlocked(PyTypeObject *tp, unsigned int version)
993993
{
994994
ASSERT_TYPE_LOCK_HELD();
995+
assert(version == 0 || (tp->tp_versions_used != _Py_ATTR_CACHE_UNUSED));
995996
#ifndef Py_GIL_DISABLED
996997
PyInterpreterState *interp = _PyInterpreterState_GET();
997998
// lookup the old version and set to null
@@ -1148,6 +1149,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11481149
PyObject *b = PyTuple_GET_ITEM(bases, i);
11491150
PyTypeObject *cls = _PyType_CAST(b);
11501151

1152+
if (cls->tp_versions_used >= _Py_ATTR_CACHE_UNUSED) {
1153+
goto clear;
1154+
}
1155+
11511156
if (!is_subtype_with_mro(lookup_tp_mro(type), type, cls)) {
11521157
goto clear;
11531158
}
@@ -1156,7 +1161,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11561161

11571162
clear:
11581163
assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
1159-
set_version_unlocked(type, 0); /* 0 is not a valid version tag */
1164+
set_version_unlocked(type, 0); /* 0 is not a valid version tag */
1165+
type->tp_versions_used = _Py_ATTR_CACHE_UNUSED;
11601166
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
11611167
// This field *must* be invalidated if the type is modified (see the
11621168
// comment on struct _specialization_cache):
@@ -1208,6 +1214,9 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp)
12081214

12091215

12101216
#define MAX_VERSIONS_PER_CLASS 1000
1217+
#if _Py_ATTR_CACHE_UNUSED < MAX_VERSIONS_PER_CLASS
1218+
#error "_Py_ATTR_CACHE_UNUSED must be bigger than max"
1219+
#endif
12111220

12121221
static int
12131222
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
@@ -1225,6 +1234,7 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
12251234
return 0;
12261235
}
12271236
if (type->tp_versions_used >= MAX_VERSIONS_PER_CLASS) {
1237+
/* (this includes `tp_versions_used == _Py_ATTR_CACHE_UNUSED`) */
12281238
return 0;
12291239
}
12301240

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy