Repository navigation
__annotations__ are not inherited in 3.10 while they are in 3.8 #99535
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Nov 16, 2022 I think we don't have tests for this 🤭
At least, I am not able to find them.
We have tests forTypedDictandNamedTuplethat inherits annotations from parents.But, not for:
- regular classes
- dataclasses
Right now
TypedDicts andNamedTuples inherit all annotations from parents. Classes are inconsistent to them.Should we treat this as a bug? 🤔
3.12
mainbranch:>>> class A: ... x: int ... >>> class B(A): ... ... ... >>> A.__annotations__ {'x': <class 'int'>} >>> B.__annotations__ {}
3.11 is the same.
3.10 is the same
3.9.9 is different:>>> class A: ... x: int ... >>> class B(A): ... ... >>> A.__annotations__ {'x': <class 'int'>} >>> B.__annotations__ {'x': <class 'int'>}
So, yes. I think it is a bug. And should be fixed / backported to 3.10 and 3.11
Why?
Looks like empty
__annotations__attribute is populated in__dict__:>>> A.__dict__ mappingproxy({'__module__': '__main__', '__annotations__': {'x': <class 'int'>}, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}) >>> B.__dict__ mappingproxy({'__module__': '__main__', '__doc__': None, '__annotations__': {}})
Compare it to
3.9:>>> class A: ... x: int ... >>> class B: ... ... ... >>> A.__dict__ mappingproxy({'__module__': '__main__', '__annotations__': {'x': <class 'int'>}, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}) >>> B.__dict__ mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None})
I haven't checked the logs, but I suspect this is an intentional change made by @larryhastings as part of his efforts to improve
__annotations__handling. The 3.10+ behavior makes more sense to me: youCls.__annotations__should give you the annotations for the current class, not a base class.Yes, you are right. The change is intentional: #88067 (comment)
Reacted by Max KühnSo, adding tests and docs about it should be the way to go? 🤔
Reacted by Jelle Zijlstra and MonadChains@sobolevn I've added a small test to check the behavior of annotations w.r.t. inheritance. I've also updated the docs by adding a line to remark that annotations are not inherited anymore, but I'm not sure it is the right place for it.
One additional note:
Right now TypedDicts and NamedTuples inherit all annotations from parents. Classes are inconsistent to them.
I don't think this is quite accurate. NamedTuples behave the same as normal classes as far as the
__annotations__attribute is concerned:>>> from typing import NamedTuple >>> class A(NamedTuple): ... x: int ... >>> A.__annotations__ {'x': <class 'int'>} >>> class B(A): ... pass ... >>> B.__annotations__ {} >>> class C(A): ... y: str ... >>> C.__annotations__ {'y': <class 'str'>}
i.e.
__annotations__are not inherited, the same as for normal classes. Annotations from base classes are still considered when deciding what fields the subclass should have, though, which is something specific to the use of annotations byNamedTuple. (The same is true for dataclasses.)TypedDicts do indeed "inherit"
__annotations__, but this is a special case because of TypedDicts having very unusual inheritance behavior. When you inherit a TypedDict, all you get is its__annotations__; the base class itself does not even appear in your MRO! Anything subclassingTypedDictor any subclass ofTypedDictends up simply inheritingdict:>>> from typing import TypedDict >>> class A(TypedDict): ... x: int ... >>> A.__annotations__ {'x': <class 'int'>} >>> class B(A): ... pass ... >>> B.__annotations__ {'x': <class 'int'>} >>> B.mro() [<class '__main__.B'>, <class 'dict'>, <class 'object'>] >>> A.mro() [<class '__main__.A'>, <class 'dict'>, <class 'object'>]
So in this special case "inheritance" of the
__annotations__attribute is necessary because the inheritance hierarchy itself is not preserved.- added a commit that references this issue
on Dec 24, 2022 - added a commit that references this issue
on Dec 24, 2022 - added a commit that references this issue
on Dec 27, 2022 - added a commit that references this issue
on Dec 28, 2022 The pull request introduced a documentation error for
typing.get_type_hints.typing.get_type_hintsrecursively merges annotations from parent classes, which has always been intended behavior. The pull request mixed up this behavior with the__annotations__inheritance change, and added an incorrect note to thetyping.get_type_hintsdocumentation claiming thattyping.get_type_hintsno longer does this.Reacted by Alex Waygood, Carl Meyer and Jelmer
Bug report
Python 3.8.13 inherits annotations from a subclass:
Python 3.10.8 does not:
Can't find anything related to this in the changelogs, release notes, stackoverflow etc. Is this known/expected behaviour?
Linked PRs