diff options
-rw-r--r-- | Lib/test/test_typing.py | 6 | ||||
-rw-r--r-- | Lib/typing.py | 20 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst | 1 |
3 files changed, 21 insertions, 6 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 398add0..7f96aff 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -2456,6 +2456,12 @@ class ForwardRefTests(BaseTestCase): self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': tuple[T]}) + def test_double_forward(self): + def foo(a: 'List[\'int\']'): + pass + self.assertEqual(get_type_hints(foo, globals(), locals()), + {'a': List[int]}) + def test_forward_recursion_actually(self): def namespace1(): a = typing.ForwardRef('A') diff --git a/Lib/typing.py b/Lib/typing.py index fd657ca..5da032b 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -244,14 +244,16 @@ def _tp_cache(func): return inner -def _eval_type(t, globalns, localns): +def _eval_type(t, globalns, localns, recursive_guard=frozenset()): """Evaluate all forward reverences in the given type t. For use of globalns and localns see the docstring for get_type_hints(). + recursive_guard is used to prevent prevent infinite recursion + with recursive ForwardRef. """ if isinstance(t, ForwardRef): - return t._evaluate(globalns, localns) + return t._evaluate(globalns, localns, recursive_guard) if isinstance(t, (_GenericAlias, GenericAlias)): - ev_args = tuple(_eval_type(a, globalns, localns) for a in t.__args__) + ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__) if ev_args == t.__args__: return t if isinstance(t, GenericAlias): @@ -477,7 +479,9 @@ class ForwardRef(_Final, _root=True): self.__forward_value__ = None self.__forward_is_argument__ = is_argument - def _evaluate(self, globalns, localns): + def _evaluate(self, globalns, localns, recursive_guard): + if self.__forward_arg__ in recursive_guard: + return self if not self.__forward_evaluated__ or localns is not globalns: if globalns is None and localns is None: globalns = localns = {} @@ -485,10 +489,14 @@ class ForwardRef(_Final, _root=True): globalns = localns elif localns is None: localns = globalns - self.__forward_value__ = _type_check( + type_ =_type_check( eval(self.__forward_code__, globalns, localns), "Forward references must evaluate to types.", - is_argument=self.__forward_is_argument__) + is_argument=self.__forward_is_argument__, + ) + self.__forward_value__ = _eval_type( + type_, globalns, localns, recursive_guard | {self.__forward_arg__} + ) self.__forward_evaluated__ = True return self.__forward_value__ diff --git a/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst b/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst new file mode 100644 index 0000000..c78b24d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst @@ -0,0 +1 @@ +Recursive evaluation of `typing.ForwardRef` in `get_type_hints`.
\ No newline at end of file |