Skip to content

Stale entries in MRO cache if MRO contains non-base classes #127773

Description

@colesbury

Bug report

For example the following program fails with either assert WeirdClass.value == 2 or assert WeirdClass.value == 3 in recent Python versions:

import sys

class Base:
    value = 1

class Meta(type):
    def mro(cls):
        return (cls, Base, object)

class WeirdClass(metaclass=Meta):
    pass

assert Base.value == 1
assert WeirdClass.value == 1

Base.value = 2
assert Base.value == 2
assert WeirdClass.value == 2

Base.value = 3
assert Base.value == 3
assert WeirdClass.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+:

cpython/Objects/typeobject.c

Lines 1107 to 1112 in 5c89adf

static void
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:

def test_freeze_meta(self):
"""test PyType_Freeze() with overridden MRO"""
type_freeze = _testcapi.type_freeze
class Base:
value = 1
class Meta(type):
def mro(cls):
return (cls, Base, object)
class FreezeThis(metaclass=Meta):
"""This has `Base` in the MRO, but not tp_bases"""
self.assertEqual(FreezeThis.value, 1)
with self.assertRaises(TypeError):
type_freeze(FreezeThis)
Base.value = 2
self.assertEqual(FreezeThis.value, 2)
type_freeze(Base)
with self.assertRaises(TypeError):
Base.value = 3
type_freeze(FreezeThis)
self.assertEqual(FreezeThis.value, 2)

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    3.12only security fixes
    3.13only security fixes
    3.14bugs and security fixes
    on Dec 9, 2024
  2. colesbury commented on Dec 9, 2024

    @colesbury
    ContributorAuthor

    cc @encukou - this broke in a4760cc (GH-27260), which removed Py_TPFLAGS_HAVE_VERSION_TAG.

    The reason for removing Py_TPFLAGS_HAVE_VERSION_TAG was because "the field is always present in the type struct", but the flag indicates whether a type supports the method cache (i.e, supports having a valid version tag) and types with MRO entries that aren't superclasses don't support the method cache.

  3. cfbolz commented on Dec 10, 2024

    @cfbolz
    Contributor

    Great (if very annoying) find, @colesbury! The bug even affects pypy, which has an entirely different architecture for its caches 😳

  4. self-assigned this
    on Dec 12, 2024
  5. added a commit that references this issue on Dec 20, 2024
  6. added a commit that references this issue on Jan 13, 2025
  7. colesbury commented on Feb 6, 2025

    @colesbury
    ContributorAuthor

    I think this was fixed by @encukou

  8. encukou commented on Feb 7, 2025

    @encukou
    Member

    In the main branch. I don't know if I'll get to older branches in time.

  9. reopened this on Feb 7, 2025
  10. kumaraditya303 commented on Jan 9, 2026

    @kumaraditya303
    Contributor

    @encukou do you want to backport it? Otherwise, this seems complete now.

  11. added a commit that references this issue on Jan 12, 2026
  12. encukou commented on Jan 12, 2026

    @encukou
    Member

    Oh, thanks for the ping!
    See #143729 for the backport.

  13. added a commit that references this issue on Jan 26, 2026
  14. encukou commented on Jan 26, 2026

    @encukou
    Member

    I've merged the backport to 3.13.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

3.12only security fixes3.13only security fixes3.14bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions