-
-
Notifications
You must be signed in to change notification settings - Fork 117
Support ParamSpec for TypeAliasType #449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
b6bc323
79985f3
02025d4
3199b7b
f7d79d9
c4c0e68
d6af983
0a5039b
e532429
9911ac7
e1b3095
408ae2e
621085f
b3e6b7a
c3d98c6
8255667
255de76
3037923
b8ae82e
8d2ec0a
7029d51
02fd0ba
5c0938c
b6fefb0
af0a133
f2aa35c
6b1bafb
7c1fea7
efa1214
66eebb1
df10751
889e9ae
b6b5a14
9562635
0033813
014109c
0b3ce7d
0ae4c63
144c7b8
e613294
e44fdcc
6bc1f57
e8bfa30
617656d
41a87b8
a8c4bda
249b869
e71902e
b8799ce
5bc1360
3ae9e35
9878cc0
3863c8b
8c86619
251d312
b3aa598
07ba7b7
28d1e84
a365355
fb55b88
f2d6890
054f083
eb840ef
02efbdc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3572,13 +3572,13 @@ class P(Protocol): | |
self.assertEqual(Alias, Alias2) | ||
|
||
def test_protocols_pickleable(self): | ||
global P, CP # pickle wants to reference the class by name | ||
global GlobalProto, CP # pickle wants to reference the class by name | ||
T = TypeVar('T') | ||
|
||
@runtime_checkable | ||
class P(Protocol[T]): | ||
class GlobalProto(Protocol[T]): | ||
x = 1 | ||
class CP(P[int]): | ||
class CP(GlobalProto[int]): | ||
pass | ||
|
||
c = CP() | ||
|
@@ -3591,7 +3591,7 @@ class CP(P[int]): | |
self.assertEqual(x.bar, 'abc') | ||
self.assertEqual(x.x, 1) | ||
self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) | ||
s = pickle.dumps(P) | ||
s = pickle.dumps(GlobalProto) | ||
D = pickle.loads(s) | ||
class E: | ||
x = 1 | ||
|
@@ -7165,13 +7165,52 @@ def test_attributes(self): | |
self.assertEqual(ListOrSetT.__type_params__, (T,)) | ||
self.assertEqual(ListOrSetT.__parameters__, (T,)) | ||
|
||
subscripted = ListOrSetT[int] | ||
AlexWaygood marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.assertEqual(subscripted.__name__, "ListOrSetT") | ||
self.assertEqual(subscripted.__value__, Union[List[T], Set[T]],) | ||
self.assertEqual(subscripted.__type_params__, (T, )) | ||
self.assertEqual(subscripted.__parameters__, ()) | ||
|
||
T2 = TypeVar("T2") | ||
subscriptedT = ListOrSetT[T2] | ||
self.assertEqual(subscriptedT.__name__, "ListOrSetT") | ||
self.assertEqual(subscriptedT.__value__, Union[List[T], Set[T]],) | ||
self.assertEqual(subscriptedT.__type_params__, (T, )) | ||
self.assertEqual(subscriptedT.__parameters__, (T2, )) | ||
|
||
Ts = TypeVarTuple("Ts") | ||
Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,)) | ||
self.assertEqual(Variadic.__name__, "Variadic") | ||
self.assertEqual(Variadic.__value__, Tuple[int, Unpack[Ts]]) | ||
self.assertEqual(Variadic.__type_params__, (Ts,)) | ||
self.assertEqual(Variadic.__parameters__, tuple(iter(Ts))) | ||
|
||
subscripted_tuple = Variadic[Unpack[Tuple[int, float]]] | ||
self.assertEqual(subscripted_tuple.__name__, "Variadic") | ||
self.assertEqual(subscripted_tuple.__value__, Tuple[int, Unpack[Ts]]) | ||
self.assertEqual(subscripted_tuple.__type_params__, (Ts,)) | ||
self.assertEqual(subscripted_tuple.__parameters__, ()) | ||
|
||
subscripted_tupleT = Variadic[Unpack[Tuple[int, T]]] | ||
self.assertEqual(subscripted_tupleT.__name__, "Variadic") | ||
self.assertEqual(subscripted_tupleT.__parameters__, (T, )) | ||
|
||
# Use with Callable | ||
# Use with Callable+Concatenate | ||
subscripted_callable_concat = Variadic[Callable[Concatenate[Literal["s"], P], T]] | ||
self.assertEqual(subscripted_callable_concat.__parameters__, (P, T)) | ||
|
||
subcriped_callable_tvt = Variadic[Callable[[Unpack[Ts]], T]] | ||
self.assertEqual(subcriped_callable_tvt.__parameters__, (Ts, T)) | ||
|
||
# Use with Callable+Unpack | ||
CallableTs = TypeAliasType("CallableTs", Callable[[Unpack[Ts]], Any], type_params=(Ts, )) | ||
self.assertEqual(CallableTs.__type_params__, (Ts,)) | ||
self.assertEqual(CallableTs.__parameters__, (*Ts,)) | ||
|
||
unpack_callable = CallableTs[Unpack[Tuple[int, T]]] | ||
self.assertEqual(unpack_callable.__parameters__, (T,)) | ||
|
||
def test_cannot_set_attributes(self): | ||
Simple = TypeAliasType("Simple", int) | ||
with self.assertRaisesRegex(AttributeError, "readonly attribute"): | ||
|
@@ -7232,12 +7271,13 @@ def test_or(self): | |
Alias | "Ref" | ||
|
||
def test_getitem(self): | ||
T = TypeVar("T") | ||
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,)) | ||
subscripted = ListOrSetT[int] | ||
self.assertEqual(get_args(subscripted), (int,)) | ||
self.assertIs(get_origin(subscripted), ListOrSetT) | ||
with self.assertRaises(TypeError): | ||
subscripted[str] | ||
with self.assertRaises(TypeError, msg="not a generic class"): | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
subscripted[int] | ||
|
||
still_generic = ListOrSetT[Iterable[T]] | ||
self.assertEqual(get_args(still_generic), (Iterable[T],)) | ||
|
@@ -7246,6 +7286,174 @@ def test_getitem(self): | |
self.assertEqual(get_args(fully_subscripted), (Iterable[float],)) | ||
self.assertIs(get_origin(fully_subscripted), ListOrSetT) | ||
|
||
# Test ParamSpec and Ellipsis | ||
P = ParamSpec('P') | ||
CallableP = TypeAliasType("CallableP", Callable[P, Any], type_params=(P,)) | ||
# () -> Any | ||
callable_no_arg = CallableP[[]] | ||
self.assertEqual(get_args(callable_no_arg), ([],)) | ||
# (int) -> Any | ||
callable_arg = CallableP[int] | ||
self.assertEqual(get_args(callable_arg), (int,)) | ||
|
||
callable_arg_list = CallableP[[int]] | ||
self.assertEqual(get_args(callable_arg_list), ([int],)) | ||
|
||
# (int, int) -> Any | ||
callable_arg2 = CallableP[int, int] | ||
self.assertEqual(get_args(callable_arg2), (int, int,)) | ||
|
||
callable_arg2_list = CallableP[[int, int]] | ||
self.assertEqual(get_args(callable_arg2_list), ([int, int],)) | ||
# (...) -> Any | ||
callable_ellipsis = CallableP[...] | ||
self.assertEqual(get_args(callable_ellipsis), (...,)) | ||
|
||
callable_ellipsis2 = CallableP[(...,)] | ||
self.assertEqual(callable_ellipsis, callable_ellipsis2) | ||
# (int, ...) -> Any | ||
callable_arg_more = CallableP[[int, ...]] | ||
self.assertEqual(get_args(callable_arg_more), ([int, ...],)) | ||
AlexWaygood marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# (T) -> Any | ||
callable_generic_raw = CallableP[T] | ||
self.assertEqual(get_args(callable_generic_raw), (T,)) | ||
self.assertEqual(callable_generic_raw.__parameters__, (T,)) | ||
|
||
# Usage with Concatenate | ||
callable_concat = CallableP[Concatenate[int, P]] | ||
self.assertEqual(callable_concat.__parameters__, (P,)) | ||
if TYPING_3_11_0: | ||
self.assertEqual(get_args(callable_concat), (Concatenate[int, P],)) | ||
concat_usage = callable_concat[str] | ||
self.assertEqual(get_args(concat_usage), ((int, str),)) | ||
self.assertEqual(concat_usage, callable_concat[[str]]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this block be removed? This is more about GenericAlias usage here and how Concatenate/Unpack is handled. I think the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since using |
||
elif TYPING_3_10_0: | ||
self.assertEqual(get_args(callable_concat), (int, P,)) | ||
with self.assertRaises(TypeError, msg="Parameters to generic types must be types"): | ||
callable_concat[str] | ||
concat_usage = callable_concat[[str]] | ||
self.assertEqual(get_args(concat_usage), (int, [str])) | ||
else: | ||
self.assertEqual(get_args(callable_concat), (int, P,)) | ||
with self.assertRaises(TypeError, msg="Parameters to generic types must be types"): | ||
callable_concat[[str]] | ||
concat_usage = callable_concat[str] | ||
self.assertEqual(get_args(concat_usage), (int, str)) | ||
|
||
# More complex cases | ||
Ts = TypeVarTuple("Ts") | ||
Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,)) | ||
mixed_subscripedPT = Variadic[Callable[Concatenate[int, P], T]] | ||
self.assertEqual(get_args(mixed_subscripedPT), (Callable[Concatenate[int, P], T],)) | ||
|
||
|
||
@skipUnless(TYPING_3_11_0, "__args__ behaves differently") | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def test_311_substitution(self): | ||
# To pass these tests alias.__args__ in TypeAliasType.__getitem__ needs adjustment | ||
# Unpack and Concatenate are unpacked in versions before | ||
T = TypeVar("T") | ||
Ts = TypeVarTuple("Ts") | ||
|
||
CallableTs = TypeAliasType("CallableTs", Callable[[Unpack[Ts]], Any], type_params=(Ts, )) | ||
unpack_callable = CallableTs[Unpack[Tuple[int, T]]] | ||
self.assertEqual(get_args(unpack_callable), (Unpack[Tuple[int, T]],)) | ||
|
||
P = ParamSpec('P') | ||
CallableP = TypeAliasType("CallableP", Callable[P, T], type_params=(P, T)) | ||
callable_concat = CallableP[Concatenate[int, P], Any] | ||
self.assertEqual(get_args(callable_concat), (Concatenate[int, P], Any)) | ||
|
||
@skipUnless(TYPING_3_12_0, "__args__ behaves differently") | ||
def test_312_substitution(self): | ||
# To pass these tests alias.__args__ in TypeAliasType.__getitem__ needs to be adjustment | ||
# Would raise: TypeError: Substitution of bare TypeVarTuple is not supported | ||
T = TypeVar("T") | ||
Ts = TypeVarTuple("Ts") | ||
Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,)) | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
subcriped_callable_tvt = Variadic[Callable[[Unpack[Ts]], T]] | ||
variadic_tvt_callableA = subcriped_callable_tvt[str, object] | ||
variadic_tvt_callableA2 = subcriped_callable_tvt[Unpack[Tuple[str]], object] | ||
self.assertEqual(variadic_tvt_callableA, variadic_tvt_callableA2) | ||
|
||
variadic_tvt_callableB = subcriped_callable_tvt[[str, int], object] | ||
variadic_tvt_callableB2 = subcriped_callable_tvt[Unpack[Tuple[str, int]], object] | ||
variadic_tvt_callableB3 = subcriped_callable_tvt[str, int, object] | ||
self.assertNotEqual(variadic_tvt_callableB, variadic_tvt_callableB2) | ||
self.assertEqual(variadic_tvt_callableB2, variadic_tvt_callableB3) | ||
|
||
def test_invalid_cases(self): | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# NOTE: If these cases fail the specificiation might have changed | ||
# some of the cases could be seen as valid but are currently not | ||
|
||
# More parameters | ||
T = TypeVar("T") | ||
T2 = TypeVar("T2") | ||
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,)) | ||
|
||
too_many = ListOrSetT[int, bool] | ||
self.assertEqual(get_args(too_many), (int, bool)) | ||
self.assertEqual(too_many.__parameters__, ()) | ||
|
||
# Not enough parameters | ||
ListOrSet2T = TypeAliasType("ListOrSet2T", Union[List[T], Set[T2]], type_params=(T, T2)) | ||
not_enough = ListOrSet2T[int] | ||
self.assertEqual(get_args(not_enough), (int,)) | ||
self.assertEqual(not_enough.__parameters__, ()) | ||
|
||
not_enough2 = ListOrSet2T[T] | ||
self.assertEqual(get_args(not_enough2), (T,)) | ||
self.assertEqual(not_enough2.__parameters__, (T,)) | ||
# ParamSpec | ||
P = ParamSpec('P') | ||
CallableP = TypeAliasType("CallableP", Callable[P, T], type_params=(P,)) | ||
|
||
callable_not_enough = CallableP[int] | ||
self.assertEqual(callable_not_enough.__parameters__, ()) | ||
self.assertEqual(get_args(callable_not_enough), (int, )) | ||
|
||
callable_too_many = CallableP[str, float, T2, int] | ||
self.assertEqual(callable_too_many.__parameters__, (T2, )) | ||
self.assertEqual(get_args(callable_too_many), (str, float, T2, int, )) | ||
|
||
# Cases that result in parameterless variable | ||
|
||
# Callable | ||
Daraan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
CallableT = CallableP[[T]] | ||
self.assertEqual(get_args(CallableT), ([T],)) | ||
self.assertEqual(CallableT.__parameters__, ()) | ||
with self.assertRaises(TypeError, msg="is not a generic class"): | ||
CallableT[str] | ||
|
||
ImplicitConcatP = CallableP[[int, P]] | ||
self.assertEqual(get_args(ImplicitConcatP), ([int, P],)) | ||
self.assertEqual(ImplicitConcatP.__parameters__, ()) | ||
with self.assertRaises(TypeError, msg="is not a generic class"): | ||
ImplicitConcatP[str] | ||
|
||
# TypeVarTuple | ||
Ts = TypeVarTuple("Ts") | ||
Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,)) | ||
|
||
# No Tuple, but list | ||
invalid_tupleT = Variadic[[int, T]] | ||
self.assertEqual(invalid_tupleT.__parameters__, ()) | ||
self.assertEqual(get_args(invalid_tupleT), ([int, T],)) | ||
|
||
with self.assertRaises(TypeError, msg="is not a generic class"): | ||
invalid_tupleT[str] | ||
|
||
|
||
@skipIf(TYPING_3_11_0, "Most cases are allowed in 3.11+") | ||
def test_invalid_cases_before_3_11(self): | ||
T = TypeVar("T") | ||
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,)) | ||
with self.assertRaises(TypeError): | ||
ListOrSetT[Generic[T]] | ||
with self.assertRaises(TypeError): | ||
ListOrSetT[(Generic[T], )] | ||
|
||
|
||
def test_pickle(self): | ||
global Alias | ||
Alias = TypeAliasType("Alias", int) | ||
|
Uh oh!
There was an error while loading. Please reload this page.