Skip to content

Commit cc7fc1c

Browse files
loosen generics, allow typevar-like subst, flatten out args if Callable
1 parent 9727e2a commit cc7fc1c

File tree

2 files changed

+32
-25
lines changed

2 files changed

+32
-25
lines changed

Lib/test/test_typing.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,8 +1739,6 @@ def test_extended_generic_rules_eq(self):
17391739
self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]])
17401740
with self.assertRaises(TypeError):
17411741
Tuple[T, int][()]
1742-
with self.assertRaises(TypeError):
1743-
Tuple[T, U][T, ...]
17441742

17451743
self.assertEqual(Union[T, int][int], int)
17461744
self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str])
@@ -1752,10 +1750,6 @@ class Derived(Base): ...
17521750

17531751
self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT])
17541752
self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]])
1755-
with self.assertRaises(TypeError):
1756-
Callable[[T], U][..., int]
1757-
with self.assertRaises(TypeError):
1758-
Callable[[T], U][[], int]
17591753

17601754
def test_extended_generic_rules_repr(self):
17611755
T = TypeVar('T')
@@ -4363,6 +4357,13 @@ class Z(Generic[P]):
43634357
self.assertEqual(G5.__parameters__, G6.__parameters__)
43644358
self.assertEqual(G5, G6)
43654359

4360+
def test_var_substitution(self):
4361+
T = TypeVar("T")
4362+
P = ParamSpec("P")
4363+
C1 = Callable[P, T]
4364+
self.assertEqual(C1[int, str], Callable[[int], str])
4365+
self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float])
4366+
43664367

43674368
class ConcatenateTests(BaseTestCase):
43684369
def test_basics(self):

Lib/typing.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,21 @@ def _check_generic(cls, parameters, elen):
212212
raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
213213
f" actual {alen}, expected {elen}")
214214

215+
def _prepare_paramspec_params(cls, params):
216+
"""Prepares the parameters for a Generic containing ParamSpec
217+
variables (internal helper).
218+
"""
219+
# Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.
220+
if len(cls.__parameters__) == 1 and len(params) > 1:
221+
return params,
222+
else:
223+
_params = []
224+
# Convert lists to tuples to help other libraries cache the results.
225+
for p, tvar in zip(params, cls.__parameters__):
226+
if isinstance(tvar, ParamSpec) and isinstance(p, list):
227+
p = tuple(p)
228+
_params.append(p)
229+
return tuple(_params)
215230

216231
def _deduplicate(params):
217232
# Weed out strict duplicates, preserving the first of each occurrence.
@@ -881,21 +896,26 @@ def __getitem__(self, params):
881896
raise TypeError(f"Cannot subscript already-subscripted {self}")
882897
if not isinstance(params, tuple):
883898
params = (params,)
884-
msg = "Parameters to generic types must be types."
885-
params = tuple(_type_check(p, msg) for p in params)
899+
params = tuple(_type_convert(p) for p in params)
900+
if any(isinstance(t, ParamSpec) for t in self.__parameters__):
901+
params = _prepare_paramspec_params(self, params)
886902
_check_generic(self, params, len(self.__parameters__))
887903

888904
subst = dict(zip(self.__parameters__, params))
889905
new_args = []
890906
for arg in self.__args__:
891-
if isinstance(arg, TypeVar):
907+
if isinstance(arg, (TypeVar, ParamSpec)):
892908
arg = subst[arg]
893909
elif isinstance(arg, (_GenericAlias, GenericAlias)):
894910
subparams = arg.__parameters__
895911
if subparams:
896912
subargs = tuple(subst[x] for x in subparams)
897913
arg = arg[subargs]
898-
new_args.append(arg)
914+
# Required to flatten out the args for CallableGenericAlias
915+
if self.__origin__ == collections.abc.Callable and isinstance(arg, tuple):
916+
new_args.extend(arg)
917+
else:
918+
new_args.append(arg)
899919
return self.copy_with(tuple(new_args))
900920

901921
def copy_with(self, params):
@@ -1140,22 +1160,8 @@ def __class_getitem__(cls, params):
11401160
else:
11411161
# Subscripting a regular Generic subclass.
11421162

1143-
# Code below handles PEP 612 ParamSpec.
11441163
if any(isinstance(t, ParamSpec) for t in cls.__parameters__):
1145-
# Special case where Z[[int, str, bool]] == Z[int, str, bool]
1146-
# in PEP 612.
1147-
if len(cls.__parameters__) == 1 and len(params) > 1:
1148-
params = (params,)
1149-
else:
1150-
_params = []
1151-
# Convert lists to tuples to help other libraries cache the
1152-
# results.
1153-
for p, tvar in zip(params, cls.__parameters__):
1154-
if isinstance(tvar, ParamSpec) and isinstance(p, list):
1155-
p = tuple(p)
1156-
_params.append(p)
1157-
params = tuple(_params)
1158-
1164+
params = _prepare_paramspec_params(cls, params)
11591165
_check_generic(cls, params, len(cls.__parameters__))
11601166
return _GenericAlias(cls, params)
11611167

0 commit comments

Comments
 (0)