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?