bpo-40396: Support GenericAlias in the typing functions. (GH-19718)
This commit is contained in:
parent
cfaf4c09ab
commit
68b352a698
|
@ -22,7 +22,7 @@ from typing import NewType
|
|||
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 @@ class GenericTests(BaseTestCase):
|
|||
|
||||
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 @@ class ForwardRefTests(BaseTestCase):
|
|||
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 @@ class GetTypeHintTests(BaseTestCase):
|
|||
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 @@ class GetTypeHintTests(BaseTestCase):
|
|||
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 GetUtilitiesTestCase(TestCase):
|
|||
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 GetUtilitiesTestCase(TestCase):
|
|||
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):
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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]``.
|
Loading…
Reference in New Issue