Description
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
:cpython/Lib/test/test_class.py
Line 862 in 49365bd
-
Correct constant definition in
object.h
:Lines 532 to 535 in 49365bd
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.