[Python-checkins] bpo-40396: Support GenericAlias in the typing functions. (GH-19718)
Serhiy Storchaka
webhook-mailer at python.org
Sun Apr 26 14:21:16 EDT 2020
https://github.com/python/cpython/commit/68b352a6982f51e19bf9b9f4ae61b34f5864d131
commit: 68b352a6982f51e19bf9b9f4ae61b34f5864d131
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-04-26T21:21:08+03:00
summary:
bpo-40396: Support GenericAlias in the typing functions. (GH-19718)
files:
A Misc/NEWS.d/next/Library/2020-04-26-19-07-40.bpo-40396.Fn-is1.rst
M Lib/test/test_typing.py
M Lib/typing.py
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index b3a671732167e..46e0ea559ddb4 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -22,7 +22,7 @@
from typing import NamedTuple, TypedDict
from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
-from typing import Annotated
+from typing import Annotated, ForwardRef
import abc
import typing
import weakref
@@ -1756,11 +1756,17 @@ def test_extended_generic_rules_repr(self):
def test_generic_forward_ref(self):
def foobar(x: List[List['CC']]): ...
+ def foobar2(x: list[list[ForwardRef('CC')]]): ...
class CC: ...
self.assertEqual(
get_type_hints(foobar, globals(), locals()),
{'x': List[List[CC]]}
)
+ self.assertEqual(
+ get_type_hints(foobar2, globals(), locals()),
+ {'x': list[list[CC]]}
+ )
+
T = TypeVar('T')
AT = Tuple[T, ...]
def barfoo(x: AT): ...
@@ -2446,6 +2452,12 @@ def foo(a: Tuple['T']):
self.assertEqual(get_type_hints(foo, globals(), locals()),
{'a': Tuple[T]})
+ def foo(a: tuple[ForwardRef('T')]):
+ pass
+
+ self.assertEqual(get_type_hints(foo, globals(), locals()),
+ {'a': tuple[T]})
+
def test_forward_recursion_actually(self):
def namespace1():
a = typing.ForwardRef('A')
@@ -2909,6 +2921,18 @@ def foobar(x: List['X']): ...
get_type_hints(foobar, globals(), locals(), include_extras=True),
{'x': List[Annotated[int, (1, 10)]]}
)
+
+ def foobar(x: list[ForwardRef('X')]): ...
+ X = Annotated[int, (1, 10)]
+ self.assertEqual(
+ get_type_hints(foobar, globals(), locals()),
+ {'x': list[int]}
+ )
+ self.assertEqual(
+ get_type_hints(foobar, globals(), locals(), include_extras=True),
+ {'x': list[Annotated[int, (1, 10)]]}
+ )
+
BA = Tuple[Annotated[T, (1, 0)], ...]
def barfoo(x: BA): ...
self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...])
@@ -2916,12 +2940,22 @@ def barfoo(x: BA): ...
get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
BA
)
+
+ BA = tuple[Annotated[T, (1, 0)], ...]
+ def barfoo(x: BA): ...
+ self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], tuple[T, ...])
+ self.assertIs(
+ get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'],
+ BA
+ )
+
def barfoo2(x: typing.Callable[..., Annotated[List[T], "const"]],
y: typing.Union[int, Annotated[T, "mutable"]]): ...
self.assertEqual(
get_type_hints(barfoo2, globals(), locals()),
{'x': typing.Callable[..., List[T]], 'y': typing.Union[int, T]}
)
+
BA2 = typing.Callable[..., List[T]]
def barfoo3(x: BA2): ...
self.assertIs(
@@ -2972,6 +3006,9 @@ class C(Generic[T]): pass
self.assertIs(get_origin(Generic[T]), Generic)
self.assertIs(get_origin(List[Tuple[T, T]][int]), list)
self.assertIs(get_origin(Annotated[T, 'thing']), Annotated)
+ self.assertIs(get_origin(List), list)
+ self.assertIs(get_origin(list[int]), list)
+ self.assertIs(get_origin(list), None)
def test_get_args(self):
T = TypeVar('T')
@@ -2993,6 +3030,9 @@ class C(Generic[T]): pass
self.assertEqual(get_args(Tuple[int, ...]), (int, ...))
self.assertEqual(get_args(Tuple[()]), ((),))
self.assertEqual(get_args(Annotated[T, 'one', 2, ['three']]), (T, 'one', 2, ['three']))
+ self.assertEqual(get_args(List), (typing.T,))
+ self.assertEqual(get_args(list[int]), (int,))
+ self.assertEqual(get_args(list), ())
class CollectionsAbcTests(BaseTestCase):
diff --git a/Lib/typing.py b/Lib/typing.py
index 1b13aed22a38c..1aefcb8a8a27d 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -191,7 +191,7 @@ def _subs_tvars(tp, tvars, subs):
"""Substitute type variables 'tvars' with substitutions 'subs'.
These two must have the same length.
"""
- if not isinstance(tp, _GenericAlias):
+ if not isinstance(tp, (_GenericAlias, GenericAlias)):
return tp
new_args = list(tp.__args__)
for a, arg in enumerate(tp.__args__):
@@ -203,7 +203,10 @@ def _subs_tvars(tp, tvars, subs):
new_args[a] = _subs_tvars(arg, tvars, subs)
if tp.__origin__ is Union:
return Union[tuple(new_args)]
- return tp.copy_with(tuple(new_args))
+ if isinstance(tp, GenericAlias):
+ return GenericAlias(tp.__origin__, tuple(new_args))
+ else:
+ return tp.copy_with(tuple(new_args))
def _check_generic(cls, parameters):
@@ -278,6 +281,11 @@ def _eval_type(t, globalns, localns):
res = t.copy_with(ev_args)
res._special = t._special
return res
+ if isinstance(t, GenericAlias):
+ ev_args = tuple(_eval_type(a, globalns, localns) for a in t.__args__)
+ if ev_args == t.__args__:
+ return t
+ return GenericAlias(t.__origin__, ev_args)
return t
@@ -1368,6 +1376,11 @@ def _strip_annotations(t):
res = t.copy_with(stripped_args)
res._special = t._special
return res
+ if isinstance(t, GenericAlias):
+ stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
+ if stripped_args == t.__args__:
+ return t
+ return GenericAlias(t.__origin__, stripped_args)
return t
@@ -1387,7 +1400,7 @@ def get_origin(tp):
"""
if isinstance(tp, _AnnotatedAlias):
return Annotated
- if isinstance(tp, _GenericAlias):
+ if isinstance(tp, (_GenericAlias, GenericAlias)):
return tp.__origin__
if tp is Generic:
return Generic
@@ -1407,9 +1420,9 @@ def get_args(tp):
"""
if isinstance(tp, _AnnotatedAlias):
return (tp.__origin__,) + tp.__metadata__
- if isinstance(tp, _GenericAlias):
+ if isinstance(tp, (_GenericAlias, GenericAlias)):
res = tp.__args__
- if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+ if tp.__origin__ is collections.abc.Callable and res[0] is not Ellipsis:
res = (list(res[:-1]), res[-1])
return res
return ()
diff --git a/Misc/NEWS.d/next/Library/2020-04-26-19-07-40.bpo-40396.Fn-is1.rst b/Misc/NEWS.d/next/Library/2020-04-26-19-07-40.bpo-40396.Fn-is1.rst
new file mode 100644
index 0000000000000..f4273ff19663e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-04-26-19-07-40.bpo-40396.Fn-is1.rst
@@ -0,0 +1,3 @@
+Functions :func:`typing.get_origin`, :func:`typing.get_args` and
+:func:`typing.get_type_hints` support now generic aliases like
+``list[int]``.
More information about the Python-checkins
mailing list