summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_typing.py44
-rw-r--r--Lib/typing.py6
-rw-r--r--Misc/NEWS.d/next/Library/2022-01-27-11-54-16.bpo-41370.gYxCPE.rst1
3 files changed, 49 insertions, 2 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 86baed9..17d78cf 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -32,6 +32,7 @@ from typing import TypeAlias
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
from typing import TypeGuard
import abc
+import textwrap
import typing
import weakref
import types
@@ -2156,6 +2157,45 @@ class GenericTests(BaseTestCase):
def barfoo2(x: CT): ...
self.assertIs(get_type_hints(barfoo2, globals(), locals())['x'], CT)
+ def test_generic_pep585_forward_ref(self):
+ # See https://bugs.python.org/issue41370
+
+ class C1:
+ a: list['C1']
+ self.assertEqual(
+ get_type_hints(C1, globals(), locals()),
+ {'a': list[C1]}
+ )
+
+ class C2:
+ a: dict['C1', list[List[list['C2']]]]
+ self.assertEqual(
+ get_type_hints(C2, globals(), locals()),
+ {'a': dict[C1, list[List[list[C2]]]]}
+ )
+
+ # Test stringified annotations
+ scope = {}
+ exec(textwrap.dedent('''
+ from __future__ import annotations
+ class C3:
+ a: List[list["C2"]]
+ '''), scope)
+ C3 = scope['C3']
+ self.assertEqual(C3.__annotations__['a'], "List[list['C2']]")
+ self.assertEqual(
+ get_type_hints(C3, globals(), locals()),
+ {'a': List[list[C2]]}
+ )
+
+ # Test recursive types
+ X = list["X"]
+ def f(x: X): ...
+ self.assertEqual(
+ get_type_hints(f, globals(), locals()),
+ {'x': list[list[ForwardRef('X')]]}
+ )
+
def test_extended_generic_rules_subclassing(self):
class T1(Tuple[T, KT]): ...
class T2(Tuple[T, ...]): ...
@@ -3556,7 +3596,7 @@ class GetTypeHintTests(BaseTestCase):
BA = Tuple[Annotated[T, (1, 0)], ...]
def barfoo(x: BA): ...
self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...])
- self.assertIs(
+ self.assertEqual(
get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
BA
)
@@ -3564,7 +3604,7 @@ class GetTypeHintTests(BaseTestCase):
BA = tuple[Annotated[T, (1, 0)], ...]
def barfoo(x: BA): ...
self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], tuple[T, ...])
- self.assertIs(
+ self.assertEqual(
get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
BA
)
diff --git a/Lib/typing.py b/Lib/typing.py
index 27d83c5..360129e 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -336,6 +336,12 @@ def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
if isinstance(t, ForwardRef):
return t._evaluate(globalns, localns, recursive_guard)
if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)):
+ if isinstance(t, GenericAlias):
+ args = tuple(
+ ForwardRef(arg) if isinstance(arg, str) else arg
+ for arg in t.__args__
+ )
+ t = t.__origin__[args]
ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
if ev_args == t.__args__:
return t
diff --git a/Misc/NEWS.d/next/Library/2022-01-27-11-54-16.bpo-41370.gYxCPE.rst b/Misc/NEWS.d/next/Library/2022-01-27-11-54-16.bpo-41370.gYxCPE.rst
new file mode 100644
index 0000000..d9ad2af
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-27-11-54-16.bpo-41370.gYxCPE.rst
@@ -0,0 +1 @@
+:func:`typing.get_type_hints` now supports evaluating strings as forward references in :ref:`PEP 585 generic aliases <types-genericalias>`.