Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d0b664e

Browse filesBrowse files
gh-118168: Fix Unpack interaction with builtin aliases (#118169)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent d687d3f commit d0b664e
Copy full SHA for d0b664e

File tree

Expand file treeCollapse file tree

3 files changed

+39
-2
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+39
-2
lines changed

‎Lib/test/test_typing.py

Copy file name to clipboardExpand all lines: Lib/test/test_typing.py
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,38 @@ def foo(**kwargs: Unpack[Movie]): ...
978978
self.assertEqual(repr(foo.__annotations__['kwargs']),
979979
f"typing.Unpack[{__name__}.Movie]")
980980

981+
def test_builtin_tuple(self):
982+
Ts = TypeVarTuple("Ts")
983+
984+
class Old(Generic[*Ts]): ...
985+
class New[*Ts]: ...
986+
987+
PartOld = Old[int, *Ts]
988+
self.assertEqual(PartOld[str].__args__, (int, str))
989+
self.assertEqual(PartOld[*tuple[str]].__args__, (int, str))
990+
self.assertEqual(PartOld[*Tuple[str]].__args__, (int, str))
991+
self.assertEqual(PartOld[Unpack[tuple[str]]].__args__, (int, str))
992+
self.assertEqual(PartOld[Unpack[Tuple[str]]].__args__, (int, str))
993+
994+
PartNew = New[int, *Ts]
995+
self.assertEqual(PartNew[str].__args__, (int, str))
996+
self.assertEqual(PartNew[*tuple[str]].__args__, (int, str))
997+
self.assertEqual(PartNew[*Tuple[str]].__args__, (int, str))
998+
self.assertEqual(PartNew[Unpack[tuple[str]]].__args__, (int, str))
999+
self.assertEqual(PartNew[Unpack[Tuple[str]]].__args__, (int, str))
1000+
1001+
def test_unpack_wrong_type(self):
1002+
Ts = TypeVarTuple("Ts")
1003+
class Gen[*Ts]: ...
1004+
PartGen = Gen[int, *Ts]
1005+
1006+
bad_unpack_param = re.escape("Unpack[...] must be used with a tuple type")
1007+
with self.assertRaisesRegex(TypeError, bad_unpack_param):
1008+
PartGen[Unpack[list[int]]]
1009+
with self.assertRaisesRegex(TypeError, bad_unpack_param):
1010+
PartGen[Unpack[List[int]]]
1011+
1012+
9811013
class TypeVarTupleTests(BaseTestCase):
9821014

9831015
def assertEndsWith(self, string, tail):

‎Lib/typing.py

Copy file name to clipboardExpand all lines: Lib/typing.py
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,8 +1786,9 @@ def __typing_unpacked_tuple_args__(self):
17861786
assert self.__origin__ is Unpack
17871787
assert len(self.__args__) == 1
17881788
arg, = self.__args__
1789-
if isinstance(arg, _GenericAlias):
1790-
assert arg.__origin__ is tuple
1789+
if isinstance(arg, (_GenericAlias, types.GenericAlias)):
1790+
if arg.__origin__ is not tuple:
1791+
raise TypeError("Unpack[...] must be used with a tuple type")
17911792
return arg.__args__
17921793
return None
17931794

+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix incorrect argument substitution when :data:`typing.Unpack` is used with
2+
the builtin :class:`tuple`. :data:`!typing.Unpack` now raises
3+
:exc:`TypeError` when used with certain invalid types. Patch by Jelle
4+
Zijlstra.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.