This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: get_type_hints fails to resolve forward references in nested function
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.10, Python 3.9, Python 3.8, Python 3.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: cjw296, gvanrossum, levkivskyi
Priority: normal Keywords: 3.6regression

Created on 2020-09-27 13:58 by cjw296, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (4)
msg377569 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-09-27 13:58
Reproducer:

    def test_forward_type_references(self):
        def foo(a: 'Foo') -> 'Bar': pass

        class Foo: pass
        class Bar: pass

        get_type_hints(foo)


The above gives the following exception, rather than resolving the type:

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py:1001: in get_type_hints
    value = _eval_type(value, globalns, localns)
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py:260: in _eval_type
    return t._evaluate(globalns, localns)
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/typing.py:464: in _evaluate
    eval(self.__forward_code__, globalns, localns),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

>   ???
E   NameError: name 'Foo' is not defined
msg377578 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-09-27 19:59
This cannot be helped (unless we were to add an ugly sys._getframe() call to get_type_hints(), which I don't want to do). The solution is to pass `localns=locals()`, e.g.

get_type_hints(foo, None, locals())

or

get_type_hints(foo, localns=locals())
msg377579 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-09-27 21:23
The tough one is that no-one wants an ugly sys._getframe() call, but by avoiding it in the standard library, we force each library that needs this to have the ugly sys._getframe() call rather than it being an unpleasant implementation detail of get_type_hints that users of the function don't have to know about.

That said, I've only hit this so far when writing a unit test, but will update this issue if I see real-world cases of this.
msg377581 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-09-27 22:00
I've only seen it in test cases too -- that's one of the very few
situations where it makes sense to define a class inside a function.
(Creating a class is an expensive operation, so any function that expects
to be called more than once is better off moving the class out of the
function.)
History
Date User Action Args
2022-04-11 14:59:36adminsetgithub: 86038
2020-09-27 22:00:19gvanrossumsetmessages: + msg377581
2020-09-27 21:23:14cjw296setmessages: + msg377579
2020-09-27 19:59:08gvanrossumsetstatus: open -> closed
messages: + msg377578

keywords: + 3.6regression
resolution: not a bug
stage: resolved
2020-09-27 15:29:02xtreaksetnosy: + gvanrossum, levkivskyi
2020-09-27 13:58:04cjw296create