Skip to content

Incorrect value for Py_TPFLAGS_MANAGED_DICT in Lib/test/test_class.py #136535

Open
@ever0de

Description

@ever0de

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

  1. Incorrect constant in test_class.py:

  2. Correct constant definition in object.h:

    • cpython/Include/object.h

      Lines 532 to 535 in 49365bd

      /* 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.

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    testsTests in the Lib/test dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions