Bug report
For example the following program fails with either assert WeirdClass.value == 2 or assert WeirdClass.value == 3 in recent Python versions:
importsysclassBase:
value=1classMeta(type):
defmro(cls):
return (cls, Base, object)
classWeirdClass(metaclass=Meta):
passassertBase.value==1assertWeirdClass.value==1Base.value=2assertBase.value==2assertWeirdClass.value==2Base.value=3assertBase.value==3assertWeirdClass.value==3
Adding intervening calls to sys _clear_internal_caches() makes the test pass.
| Version | Result |
|---|
| 3.7 | OK |
| 3.8 | OK |
| 3.9 | OK |
| 3.10 | AssertionError: assert WeirdClass.value == 2 |
| 3.11 | AssertionError: assert WeirdClass.value == 2 |
| 3.12 | AssertionError: assert WeirdClass.value == 2 |
| 3.13 | AssertionError: assert WeirdClass.value == 3 |
| main | AssertionError: assert WeirdClass.value == 3 |
We have code that checks for this case, but it hasn't worked properly in Python 3.10+:
| staticvoid |
| type_mro_modified(PyTypeObject*type, PyObject*bases) { |
| /* |
| Check that all base classes or elements of the MRO of type are |
| able to be cached. This function is called after the base |
| classes or mro of the type are altered. |
We also have a test that partly covers this case, but doesn't detect the bug:
| deftest_freeze_meta(self): |
| """test PyType_Freeze() with overridden MRO""" |
| type_freeze=_testcapi.type_freeze |
| |
| classBase: |
| value=1 |
| |
| classMeta(type): |
| defmro(cls): |
| return (cls, Base, object) |
| |
| classFreezeThis(metaclass=Meta): |
| """This has `Base` in the MRO, but not tp_bases""" |
| |
| self.assertEqual(FreezeThis.value, 1) |
| |
| withself.assertRaises(TypeError): |
| type_freeze(FreezeThis) |
| |
| Base.value=2 |
| self.assertEqual(FreezeThis.value, 2) |
| |
| type_freeze(Base) |
| withself.assertRaises(TypeError): |
| Base.value=3 |
| type_freeze(FreezeThis) |
| self.assertEqual(FreezeThis.value, 2) |
Linked PRs
Bug report
For example the following program fails with either
assert WeirdClass.value == 2orassert WeirdClass.value == 3in recent Python versions:Adding intervening calls to
sys _clear_internal_caches()makes the test pass.AssertionError: assert WeirdClass.value == 2AssertionError: assert WeirdClass.value == 2AssertionError: assert WeirdClass.value == 2AssertionError: assert WeirdClass.value == 3AssertionError: assert WeirdClass.value == 3We have code that checks for this case, but it hasn't worked properly in Python 3.10+:
cpython/Objects/typeobject.c
Lines 1107 to 1112 in 5c89adf
We also have a test that partly covers this case, but doesn't detect the bug:
cpython/Lib/test/test_capi/test_type.py
Lines 40 to 66 in 5c89adf
Linked PRs