Skip to content

get_type_hints exposes an instance of ForwardRef (internal class) in its result, with from __future__ import annotations enabled #80015

Description

@LincolnQuirk
BPO 35834
Nosy @gvanrossum, @ericvsmith, @stevendaprano, @ambv, @ilevkivskyi

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2019-01-26.17:00:18.126>
labels = ['3.8', 'type-bug', '3.7']
title = 'get_type_hints exposes an instance of ForwardRef (internal class) in its result, with `from __future__ import annotations` enabled'
updated_at = <Date 2019-01-27.10:31:35.689>
user = 'https://bugs.python.org/LincolnQuirk'

bugs.python.org fields:

activity = <Date 2019-01-27.10:31:35.689>
actor = 'levkivskyi'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = []
creation = <Date 2019-01-26.17:00:18.126>
creator = 'Lincoln Quirk'
dependencies = []
files = []
hgrepos = []
issue_num = 35834
keywords = []
message_count = 4.0
messages = ['334396', '334397', '334398', '334417']
nosy_count = 6.0
nosy_names = ['gvanrossum', 'eric.smith', 'steven.daprano', 'lukasz.langa', 'levkivskyi', 'Lincoln Quirk']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue35834'
versions = ['Python 3.7', 'Python 3.8']

Activity

  1. LincolnQuirk commented on Jan 26, 2019

    LincolnQuirkmannequin
    MannequinAuthor

    Consider this code:

    from __future__ import annotations
    
    import typing
    
    class A:
        f: 'Undef'
    
    hints = typing.get_type_hints(A)
    

    Since Undef is not defined, I should get an exception when calling get_type_hints, something like "NameError: name 'Undef' is not defined". But instead, get_type_hints returns {'f': ForwardRef('Undef')}.

    If I remove the from __future__ import annotations line, get_type_hints correctly raises this exception.

    I think the behavior should be to raise an exception in both cases.

  2. stevendaprano commented on Jan 26, 2019

    @stevendaprano
    Member

    Since Undef is not defined, I should get an exception when calling get_type_hints

    One of the motives of PEP-563 is to make it easier to use forward references. I'm not sure, but it seems to me that given that, we should not get an exception. So I think the only issue here is that the ForwardReference class is not documented, and should be.

    But I admit I'm not confident about my understanding of PEP-563 so I could be wrong.

  3. stevendaprano commented on Jan 26, 2019

    @stevendaprano
    Member

    Wait, I just noticed that PEP-563 says:

    "Note: if an annotation was a string literal already, it will still be wrapped in a string."

    https://www.python.org/dev/peps/pep-0563/#id5

    In 3.8.0a I get this:

    py> from __future__ import annotations
    py>
    py> class A:
    ... f: 'Undef'
    ...
    py> A.__annotations__
    {'f': "'Undef'"}

    which matches what the PEP says. So I expect that when calling get_type_hints it should return the unquoted string, rather than a ForwardReference.

    get_type_hints(A)

    expected {'f': 'Undef'}
    actually got {'f': ForwardRef('Undef')}

  4. ilevkivskyi commented on Jan 27, 2019

    @ilevkivskyi
    Member

    It looks like an opposite side of python/typing#508 (I wanted to work on it, but never had time to, sorry).

    This question appeared couple times before, and I think there are pros and cons for both returning a ForwardRef() and for raising a NameError. So as I proposed in the issue above, there should be a flag to get_type_hints() that controls what to do (the name of the flag, and the default are of course debatable).

    Of course, we should try to make get_type_hints() behave as much similar as possible with and without PEP-563, regardless. But my point is that currently we have this "in-between" behavior, and it is harder to maintain this "in-between" behavior against PEP-563, than two clearly defined extremes.

    So I expect that when calling get_type_hints it should return the unquoted string, rather than a ForwardReference.

    I think the values in the returned dictionary should always be types, either fully evaluated, or ForwardRefs (expecting two options is easier than expecting three).

  5. transferred this issue fromon Apr 10, 2022
  6. AlexWaygood commented on Apr 14, 2022

    @AlexWaygood
    Member

    This seems to be fixed in 3.10, and may also be fixed in earlier versions:

    C:\Users\alexw\coding>python
    Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from __future__ import annotations
    >>> import typing
    >>> class A:
    ...     f: 'Undef'
    ...
    >>> typing.get_type_hints(A)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Users\alexw\AppData\Local\Programs\Python\Python310\lib\typing.py", line 1832, in get_type_hints
        value = _eval_type(value, base_globals, base_locals)
      File "C:\Users\alexw\AppData\Local\Programs\Python\Python310\lib\typing.py", line 327, in _eval_type
        return t._evaluate(globalns, localns, recursive_guard)
      File "C:\Users\alexw\AppData\Local\Programs\Python\Python310\lib\typing.py", line 699, in _evaluate
        self.__forward_value__ = _eval_type(
      File "C:\Users\alexw\AppData\Local\Programs\Python\Python310\lib\typing.py", line 327, in _eval_type
        return t._evaluate(globalns, localns, recursive_guard)
      File "C:\Users\alexw\AppData\Local\Programs\Python\Python310\lib\typing.py", line 694, in _evaluate
        eval(self.__forward_code__, globalns, localns),
      File "<string>", line 1, in <module>
    NameError: name 'Undef' is not defined

    Cc. @LincolnQuirk

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions