Skip to content

Required/NotRequired don't work with __future__.annotations #55

Closed as not planned

Description

@jcrist

Required/NotRequired aren't properly detected when from __future__ import annotations is used, leading to incorrect __required_keys__/__optional_keys__ sets.

Reproduced with 4.3.0.

from __future__ import annotations  # comment this line out and things work correctly
from typing_extensions import NotRequired, TypedDict

class Test(TypedDict):
    a: int
    b: NotRequired[int]

assert Test.__required_keys__ == {"a"}
assert Test.__optional_keys__ == {"b"}
$ python test.py
Traceback (most recent call last):
  File "/home/jcristharif/Code/test.py", line 8, in <module>
    assert Test.__required_keys__ == {"a"}
AssertionError

Activity

  1. JelleZijlstra commented on Jul 11, 2022

    @JelleZijlstra
    Member

    What Python version are you running? Does this reproduce with typing too on Python 3.11?

  2. jcrist commented on Jul 11, 2022

    @jcrist
    Author

    I don't have (easy) access to a 3.11 build, but it reproduces with 4.3.0 on both 3.10 and 3.9.

  3. AlexWaygood commented on Jul 12, 2022

    @AlexWaygood
    Member

    I can reproduce using typing.TypedDict and typing.NotRequired on the CPython main branch (3.12), so I assume it reproduces on 3.11 using the stdlib typing module as well.

  4. yixun-alpha commented on Sep 2, 2022

    @yixun-alpha

    just ran into this issue with 4.3.0 and python 3.7.6 - any fix in the works? thanks!

  5. gvanrossum commented on Sep 2, 2022

    @gvanrossum
    Member

    So is this fixed by #60 or not? It seems to be an issue with the upstream cpython typing module as well -- did someone already file an issue there? Please link it here.

  6. JelleZijlstra commented on Sep 2, 2022

    @JelleZijlstra
    Member

    #60 would fix this in simple cases, but I think it will cause more problems than it solves. I'm inclined to wontfix this. Maybe we can revisit once there's a resolution on the PEP 563/649 saga.

  7. gvanrossum commented on Sep 2, 2022

    @gvanrossum
    Member

    It's fine to not want to solve this for now. In fact one could argue that, since the point of from __future__ import annotations is not to incur runtime costs, you get what you ask for. (Not sure if that's totally fair, but it's a possible POV.)

  8. srittau commented on Sep 2, 2024

    @srittau
    Collaborator

    I'm closing this here in favor of python/cpython#97727. It seems that the consensus is that it's too complex to fix and will go away by itself at some distant point in the future when the future import isn't required anymore.

  9. Daraan commented on Mar 28, 2025

    @Daraan
    Contributor

    I now had this problem too. Inspired by python/cpython#97727 (comment) I made these 4 utility functions to get the correct __required_keys__ / __optional_keys__, extend for __readonly_keys__ in a similar way.

    from typing_extensions import NotRequired, Required, get_origin, get_type_hints
    
    def get_explicit_required_keys(cls):
        return {k for k, v in get_type_hints(cls, include_extras=True).items() if get_origin(v) is Required}
    
    def get_explicit_unrequired_keys(cls):
        return {k for k, v in get_type_hints(cls, include_extras=True).items() if get_origin(v) is NotRequired}
    
    def get_required_keys(cls):
        return cls.__required_keys__ - get_explicit_unrequired_keys(cls)
    
    def get_optional_keys(cls):
        return cls.__optional__keys - get_explicit_required_keys(cls)
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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions