Skip to content

Commit 2bd8891

Browse files
committed
Fix Unpack Alias not being subscriptable, causing nested Unpacks to fail
1 parent 08d866b commit 2bd8891

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

src/test_typing_extensions.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5779,6 +5779,49 @@ class D(Protocol[T1, T2, Unpack[Ts]]): pass
57795779
with self.assertRaises(TypeError):
57805780
klass[int]
57815781

5782+
def test_substitution(self):
5783+
Ts = TypeVarTuple("Ts")
5784+
unpacked_str = Unpack[Ts][str]
5785+
with self.subTest("Check full unpack"):
5786+
self.assertIs(unpacked_str, str)
5787+
5788+
@skipUnless(TYPING_3_11_0, "Needs Issue #103 first")
5789+
def test_nested_unpack(self):
5790+
T = TypeVar('T')
5791+
Ts = TypeVarTuple("Ts")
5792+
Variadic = TypeAliasType("Variadic", Tuple[int, Unpack[Ts]], type_params=(Ts,))
5793+
Variadic[int, Tuple[str, int]]
5794+
5795+
TupleAliasTs = Variadic[Tuple[Unpack[Ts], int]]
5796+
5797+
# if this fails all below are likely to fail too
5798+
# Tuple[int, Tuple[str, int]]
5799+
TupleAliasTs[str]
5800+
5801+
TupleAliasTsT = Variadic[Tuple[Unpack[Ts], T]]
5802+
with self.subTest("Single parameter for tuple alias"):
5803+
# Tuple[int, Tuple[str, object]]
5804+
nested_tuple_A = TupleAliasTsT[str, object]
5805+
nested_tuple_A_unpack = TupleAliasTsT[Unpack[Tuple[str]], object]
5806+
self.assertEqual(nested_tuple_A, nested_tuple_A_unpack)
5807+
5808+
with self.subTest("Test invalid args", args=([str, int], object)):
5809+
# TypeError on some versions as types should be passed
5810+
invalid_nested_tuple = TupleAliasTsT[[str, int], object] # invalid form
5811+
with self.subTest("With Callable Ts"):
5812+
# Tuple[int, (str, int) -> object]
5813+
CallableAliasTsT = Variadic[Callable[[Unpack[Ts]], T]]
5814+
CallableAliasTsT[[str, int], object] # valid nested tuple
5815+
5816+
# Equivalent Forms
5817+
with self.subTest("Equivalence of variadic arguments"):
5818+
nested_tuple_bare = TupleAliasTsT[str, int, object]
5819+
nested_tuple_B_1xUnpack = TupleAliasTsT[Unpack[Tuple[str, int]], object]
5820+
nested_tuple_B_2xUnpack = TupleAliasTsT[Unpack[Tuple[str]], Unpack[Tuple[int]], object]
5821+
self.assertEqual(nested_tuple_B_1xUnpack, nested_tuple_bare)
5822+
self.assertEqual(nested_tuple_B_1xUnpack, nested_tuple_B_2xUnpack)
5823+
self.assertNotEqual(invalid_nested_tuple, nested_tuple_B_1xUnpack)
5824+
57825825

57835826
class TypeVarTupleTests(BaseTestCase):
57845827

src/typing_extensions.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,17 @@ def __typing_unpacked_tuple_args__(self):
24242424
return arg.__args__
24252425
return None
24262426

2427+
@property
2428+
def __typing_is_unpacked_typevartuple__(self):
2429+
assert self.__origin__ is Unpack
2430+
assert len(self.__args__) == 1
2431+
return isinstance(self.__args__[0], TypeVarTuple)
2432+
2433+
def __getitem__(self, args):
2434+
if self.__typing_is_unpacked_typevartuple__:
2435+
return args
2436+
return super().__getitem__(args)
2437+
24272438
@_UnpackSpecialForm
24282439
def Unpack(self, parameters):
24292440
item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
@@ -2436,6 +2447,17 @@ def _is_unpack(obj):
24362447
class _UnpackAlias(typing._GenericAlias, _root=True):
24372448
__class__ = typing.TypeVar
24382449

2450+
@property
2451+
def __typing_is_unpacked_typevartuple__(self):
2452+
assert self.__origin__ is Unpack
2453+
assert len(self.__args__) == 1
2454+
return isinstance(self.__args__[0], TypeVarTuple)
2455+
2456+
def __getitem__(self, args):
2457+
if self.__typing_is_unpacked_typevartuple__:
2458+
return args
2459+
return super().__getitem__(args)
2460+
24392461
class _UnpackForm(_ExtensionsSpecialForm, _root=True):
24402462
def __getitem__(self, parameters):
24412463
item = typing._type_check(parameters,

0 commit comments

Comments
 (0)