Closed
Description
Bug report
Bug description:
3.14.0b1 seems to be interpreting the annotations of a metaclass into a new format that is also interfering with the correct annotations of subclasses of that class. The subclass reports the annotations of the parent class, if it does not itself have any annotations. this only occurs if superclass is from a custom metaclass. if the subclass does have annotations, then the superclass annotations are not reported. this behavior is inconsistent with previous python versions including 3.14.0a7 and is also inconsistent with itself. see script below
from __future__ import annotations
import inspect
# a metaclass with an annotation
class MyMetaClass(type):
metadata: dict[str, str]
# dynamic class based on metaclass
MyNewClass = MyMetaClass("MyNewClass", (object,), {"metadata": {}})
# this seems new, but maybe intentional. annotations are shown for the
# dynamic class that come from the metaclass. OK
# 3.13.3: {}
# 3.14.0a7: {}
# 3.14.0b1: {'metadata': 'dict[str, str]'}
print(inspect.get_annotations(MyNewClass))
# declared class based on metaclass. Add another anno to it.
class MyOtherNewClass(metaclass=MyMetaClass):
metadata: dict[str, str] = {}
someint: int
# both pythons look good and this agrees with older pythons too.
# 3.13.3: {'metadata': 'dict[str, str]', 'someint': 'int'}
# 3.14.0a7: {'metadata': 'dict[str, str]', 'someint': 'int'}
# 3.14.0b1: {'metadata': 'dict[str, str]', 'someint': 'int'}
print(inspect.get_annotations(MyOtherNewClass))
# a control, a normal class without special metaclass.
class YetAnotherClass:
metadata: dict[str, str] = {}
someint: int
# here's where it goes wrong. If we make new classes from these
# bases that have *no* annotations, the *superclass annotations leak into the subclass*
class MySubClass(MyNewClass):
pass
# 3.13.3: {}
# 3.14.0a7: {}
# 3.14.0b1: {'metadata': 'dict[str, str]', 'someint': 'int'} # this has to be wrong
print(inspect.get_annotations(MySubClass))
# if we add *any* annotations to MySubClass, the above annos *disappear*
class MySubClass(MyNewClass):
foobar: float
# these look all correct. but 3.14.0b1's seems to be inconsistent with itself
# 3.13.3: {'foobar': 'float'}
# 3.14.0a7: {'foobar': 'float'}
# 3.14.0b1: {'foobar': 'float'} # wait what? What happened to metadata/someint from above?
print(inspect.get_annotations(MySubClass))
class MyOtherSubClass(MyOtherNewClass):
pass
# similar behaviors for declared class
# 3.13.3: {}
# 3.14.0a7: {}
# 3.14.0b1: {'metadata': 'dict[str, str]', 'someint': 'int'}
print(inspect.get_annotations(MyOtherSubClass))
# behavior does not seem to occur at all without a metaclass.
# YetAnotherClass has annotations but we do not see them from subclasses
# of that class
class MyYetAnotherSubClass(YetAnotherClass):
pass
# 3.13.3: {}
# 3.14.0a7: {}
# 3.14.0b1: {}
print(inspect.get_annotations(MyYetAnotherSubClass))
outputs:
$ python test4.py # python 3.13.3
{}
{'metadata': 'dict[str, str]', 'someint': 'int'}
{}
{'foobar': 'float'}
{}
{}
$ ~/.venv314a7/bin/python test4.py # python 3.14.0a7
{}
{'metadata': 'dict[str, str]', 'someint': 'int'}
{}
{'foobar': 'float'}
{}
{}
$ ~/.venv314/bin/python test4.py # python 3.14.0b1
{'metadata': 'dict[str, str]'}
{'metadata': 'dict[str, str]', 'someint': 'int'}
{'metadata': 'dict[str, str]'}
{'foobar': 'float'}
{'metadata': 'dict[str, str]', 'someint': 'int'}
{}
CPython versions tested on:
3.14
Operating systems tested on:
No response