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

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      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