GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — `__annotations__` are not inherited in 3.10 while they are in 3.8 · Issue #99535 · python/cpython

__annotations__ are not inherited in 3.10 while they are in 3.8 #99535

New issue
Copy link
New issue
Copy link
Closed
#99990
Closed
__annotations__ are not inherited in 3.10 while they are in 3.8#99535
#99990
Copy link
Labels
topic-typingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@henrifroese
henrifroese
opened on Nov 16, 2022
Issue body actions

Bug report

Python 3.8.13 inherits annotations from a subclass:

Python 3.8.13 (default, Oct 19 2022, 17:54:22) 
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
...     x: int
... 
>>> class B(A):
...     pass
... 
>>> print(A.__annotations__)
{'x': <class 'int'>}
>>> print(B.__annotations__)
{'x': <class 'int'>}
>>> print(B.__dict__)
{'__module__': '__main__', '__doc__': None}

Python 3.10.8 does not:

Python 3.10.8 (main, Nov  4 2022, 08:45:18) [Clang 12.0.0 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
...     x: int
... 
>>> class B(A):
...     pass
... 
>>> print(A.__annotations__)
{'x': <class 'int'>}
>>> print(B.__annotations__)
{}
>>> print(B.__dict__)
{'__module__': '__main__', '__doc__': None, '__annotations__': {}}

Can't find anything related to this in the changelogs, release notes, stackoverflow etc. Is this known/expected behaviour?

Linked PRs

  • gh-99535: Add test for inheritance of annotations and update documentation #99990
  • [3.11] gh-99535: Add test for inheritance of annotations and update documentation (GH-99990) #100507
  • [3.10] gh-99535: Add test for inheritance of annotations and update d… #100509

Activity

  1. henrifroese
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Nov 16, 2022
  2. sobolevn commented on Nov 16, 2022

    @sobolevn
    sobolevn
    on Nov 16, 2022
    Member
    More actions

    I think we don't have tests for this 🤭

    At least, I am not able to find them.
    We have tests for TypedDict and NamedTuple that inherits annotations from parents.

    But, not for:

    • regular classes
    • dataclasses

    Right now TypedDicts and NamedTuples inherit all annotations from parents. Classes are inconsistent to them.

    Should we treat this as a bug? 🤔

  3. sobolevn
    added
    topic-typing
    on Nov 16, 2022
  4. sobolevn commented on Nov 16, 2022

    @sobolevn
    sobolevn
    on Nov 16, 2022
    Member
    More actions

    3.12 main branch:

    >>> 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})
  5. JelleZijlstra commented on Nov 16, 2022

    @JelleZijlstra
    JelleZijlstra
    on Nov 16, 2022
    Member
    More actions

    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: you Cls.__annotations__ should give you the annotations for the current class, not a base class.

  6. JelleZijlstra commented on Nov 16, 2022

    @JelleZijlstra
    JelleZijlstra
    on Nov 16, 2022
    Member
    More actions

    2f2b698 and #88067

  7. sobolevn commented on Nov 16, 2022

    @sobolevn
    sobolevn
    on Nov 16, 2022
    Member
    More actions

    Yes, you are right. The change is intentional: #88067 (comment)

    Reacted by Max Kühn
  8. sobolevn commented on Nov 16, 2022

    @sobolevn
    sobolevn
    on Nov 16, 2022
    Member
    More actions

    So, adding tests and docs about it should be the way to go? 🤔

    Reacted by Jelle Zijlstra and MonadChains
  9. MonadChains commented on Dec 4, 2022

    @MonadChains
    MonadChains
    on Dec 4, 2022
    Contributor
    More actions

    @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.

  10. carljm commented on Dec 16, 2022

    @carljm
    carljm
    on Dec 16, 2022
    Member
    More actions

    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 by NamedTuple. (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 subclassing TypedDict or any subclass of TypedDict ends up simply inheriting dict:

    >>> 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.

  11. JelleZijlstra
    closed this as completedin #99990on Dec 24, 2022
  12. JelleZijlstra
    added a commit that references this issue on Dec 24, 2022

    gh-99535: Add test for inheritance of annotations and update document…

    f5b7b19
  13. miss-islington
    added a commit that references this issue on Dec 24, 2022

    pythongh-99535: Add test for inheritance of annotations and update do…

    d6cac8e
  14. JelleZijlstra
    added a commit that references this issue on Dec 24, 2022

    [3.10] pythongh-99535: Add test for inheritance of annotations and up…

    bd00e25
  15. miss-islington
    added a commit that references this issue on Dec 24, 2022

    gh-99535: Add test for inheritance of annotations and update document…

    44b664e
  16. JelleZijlstra
    added a commit that references this issue on Dec 27, 2022

    [3.10] gh-99535: Add test for inheritance of annotations and update d…

    1ffc672
  17. iritkatriel
    added a commit that references this issue on Dec 28, 2022

    Merged main into regmachine branch (#50)

    fdc73cb
  18. FrozenBob commented on Jan 1, 2023

    @FrozenBob
    FrozenBob
    on Jan 1, 2023
    Contributor
    More actions

    The pull request introduced a documentation error for typing.get_type_hints. typing.get_type_hints recursively 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 the typing.get_type_hints documentation claiming that typing.get_type_hints no longer does this.

    Reacted by Alex Waygood, Carl Meyer and Jelmer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-typingtype-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

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.