summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_typing.py6
-rw-r--r--Lib/typing.py20
-rw-r--r--Misc/NEWS.d/next/Library/2020-07-20-19-13-17.bpo-41341.wqrj8C.rst1
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