Incorrect value for `Py_TPFLAGS_MANAGED_DICT` in `Lib/test/test_class.py`
I've noticed a discrepancy in the value of the Py_TPFLAGS_MANAGED_DICT flag constant used in Lib/test/test_class.py.
Summary
The test file Lib/test/test_class.py defines a local constant Py_TPFLAGS_MANAGED_DICT = (1 << 2). However, the canonical definition in the C header Include/object.h is Py_TPFLAGS_MANAGED_DICT = (1 << 4).
The existing tests that use this constant pass only coincidentally. They test plain classes which happen to have a flag set at the (1 << 2) position, but this is not the actual managed dictionary flag.
This can be proven by a test case that uses a __slots__ class. Such a class should not have a managed dictionary, and therefore, a test checking for the MANAGED_DICT flag should result in 0. However, the test written to check for the incorrect (1 << 2) flag also results in 0, which causes an assertion to fail, proving the constant is wrong.
Proof of Incorrect Constant
The following test case demonstrates that the (1 << 2) constant is incorrect. It uses a __slots__ class, which is known not to have a managed dictionary.
The test attempts to assert that this class has the flag specified by the incorrect (1 << 2) constant. This assertion fails, providing evidence that the constant is wrong.
# In Lib/test/test_class.py import unittest class TestMisleadingConstant(unittest.TestCase): def test_slots_class_reveals_flaw_in_constant(self): # The incorrect constant as defined in test_class.py Py_TPFLAGS_MANAGED_DICT_WRONG = (1 << 2) # The correct constant from object.h Py_TPFLAGS_MANAGED_DICT_CORRECT = (1 << 4) class NoManagedDict: __slots__ = ('a',) # First, confirm that a __slots__ class correctly does NOT have # the true managed dict flag. This assertion passes. self.assertEqual( NoManagedDict.__flags__ & Py_TPFLAGS_MANAGED_DICT_CORRECT, 0, "A __slots__ class should not have the correct MANAGED_DICT flag (1<<4)" ) # Next, try to assert that this class HAS the flag at the (1<<2) position. # This assertion FAILS with "AssertionError: 0 != 4", because a __slots__ class # does not have this flag either. # This failure is the proof that the (1<<2) constant is wrong for this purpose. self.assertEqual( NoManagedDict.__flags__ & Py_TPFLAGS_MANAGED_DICT_WRONG, Py_TPFLAGS_MANAGED_DICT_WRONG, "This fails, proving the constant is wrong." )
Reference Links
-
Incorrect constant in
test_class.py:Py_TPFLAGS_MANAGED_DICT = (1 << 2)
-
Correct constant definition in
object.h:/* Placement of dict (and values) pointers are managed by the VM, not by the type. * The VM will automatically set tp_dictoffset. Implies Py_TPFLAGS_HAVE_GC. */ #define Py_TPFLAGS_MANAGED_DICT (1 << 4)
Proposed Fix
The constant Py_TPFLAGS_MANAGED_DICT in Lib/test/test_class.py should be updated to (1 << 4) to match the C header definition.
Additionally, we could add a test case for a class that does not have Py_TPFLAGS_INLINE_VALUES.
If this analysis seems correct, I would be happy to open a Pull Request to update the constant and add a corresponding test case.