PEP 749: Implementing PEP 649

When exploring the impact of order on a build from Main made this morning I found that the example @ncoghlan gave actually ends up revealing the underlying descriptor in some evaluation orders.

Script:

import sys
from itertools import permutations

print(sys.version)

def make_classes():
    class Meta(type):
        a: int

    class X(type, metaclass=Meta):
        b: float

    class Y(metaclass=X):
        c: str

    class Z(Y):
        pass

    return Meta, X, Y, Z

def demo_annotations():
    classes = make_classes()
    class_count = len(classes)
    for order in permutations(range(class_count), class_count):
        print()
        classes = make_classes()  # Regenerate classes
        for i in order:
            classes[i].__annotations__
        names = ", ".join(classes[i].__name__ for i in order)
        print(f"__annotations__ access order: {names}")
        for c in classes:
            print(f"{c.__name__}.__annotations__ = {c.__annotations__}")

demo_annotations()

From a build of 3.14a0 from Main this morning:

3.14.0a0 (heads/main:73dc1c678e, Jun 18 2024, 11:57:21) [GCC 11.4.0]

__annotations__ access order: Meta, X, Y, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Meta, X, Z, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Meta, Y, X, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Meta, Y, Z, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Meta, Z, X, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Meta, Z, Y, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: X, Meta, Y, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: X, Meta, Z, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: X, Y, Meta, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: X, Y, Z, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: X, Z, Meta, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: X, Z, Y, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {'b': <class 'float'>}

__annotations__ access order: Y, Meta, X, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Y, Meta, Z, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Y, X, Meta, Z
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {'c': <class 'str'>}

__annotations__ access order: Y, X, Z, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {'c': <class 'str'>}

__annotations__ access order: Y, Z, Meta, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Y, Z, X, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Z, Meta, X, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Z, Meta, Y, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Z, X, Meta, Y
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {}

__annotations__ access order: Z, X, Y, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'b': <class 'float'>}
Z.__annotations__ = {}

__annotations__ access order: Z, Y, Meta, X
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = <attribute '__annotations__' of 'type' objects>
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

__annotations__ access order: Z, Y, X, Meta
Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

In Python 3.13b1 or earlier for every order:

Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {'c': <class 'str'>}

The output I would have expected:

Meta.__annotations__ = {'a': <class 'int'>}
X.__annotations__ = {'b': <class 'float'>}
Y.__annotations__ = {'c': <class 'str'>}
Z.__annotations__ = {}

Is this correct? If so should this also be raised as a bug against Python 3.13?

1 Like