Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Copy link
Copy link
Open
@colesbury

Description

@colesbury
Issue body actions

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

Metadata

Metadata

Assignees

Labels

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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.