Skip to content

Commit f2bc12f

Browse files
authored
bpo-43224: Add tests for TypeVarTuple substitution in Annotated (GH-31846)
1 parent 468314c commit f2bc12f

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

Lib/test/test_typing.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5873,6 +5873,77 @@ def test_subst(self):
58735873
with self.assertRaises(TypeError):
58745874
LI[None]
58755875

5876+
def test_typevar_subst(self):
5877+
dec = "a decoration"
5878+
Ts = TypeVarTuple('Ts')
5879+
T = TypeVar('T')
5880+
T1 = TypeVar('T1')
5881+
T2 = TypeVar('T2')
5882+
5883+
A = Annotated[Tuple[Unpack[Ts]], dec]
5884+
self.assertEqual(A[int], Annotated[Tuple[int], dec])
5885+
self.assertEqual(A[str, int], Annotated[Tuple[str, int], dec])
5886+
with self.assertRaises(TypeError):
5887+
Annotated[Unpack[Ts], dec]
5888+
5889+
B = Annotated[Tuple[T, Unpack[Ts]], dec]
5890+
self.assertEqual(B[int], Annotated[Tuple[int], dec])
5891+
self.assertEqual(B[int, str], Annotated[Tuple[int, str], dec])
5892+
self.assertEqual(
5893+
B[int, str, float],
5894+
Annotated[Tuple[int, str, float], dec]
5895+
)
5896+
with self.assertRaises(TypeError):
5897+
B[()]
5898+
5899+
C = Annotated[Tuple[Unpack[Ts], T], dec]
5900+
self.assertEqual(C[int], Annotated[Tuple[int], dec])
5901+
self.assertEqual(C[int, str], Annotated[Tuple[int, str], dec])
5902+
self.assertEqual(
5903+
C[int, str, float],
5904+
Annotated[Tuple[int, str, float], dec]
5905+
)
5906+
with self.assertRaises(TypeError):
5907+
C[()]
5908+
5909+
D = Annotated[Tuple[T1, Unpack[Ts], T2], dec]
5910+
self.assertEqual(D[int, str], Annotated[Tuple[int, str], dec])
5911+
self.assertEqual(
5912+
D[int, str, float],
5913+
Annotated[Tuple[int, str, float], dec]
5914+
)
5915+
self.assertEqual(
5916+
D[int, str, bool, float],
5917+
Annotated[Tuple[int, str, bool, float], dec]
5918+
)
5919+
with self.assertRaises(TypeError):
5920+
D[int]
5921+
5922+
# Now let's try creating an alias from an alias.
5923+
5924+
Ts2 = TypeVarTuple('Ts2')
5925+
T3 = TypeVar('T3')
5926+
T4 = TypeVar('T4')
5927+
5928+
E = D[T3, Unpack[Ts2], T4]
5929+
self.assertEqual(
5930+
E,
5931+
Annotated[Tuple[T3, Unpack[Ts2], T4], dec]
5932+
)
5933+
self.assertEqual(
5934+
E[int, str], Annotated[Tuple[int, str], dec]
5935+
)
5936+
self.assertEqual(
5937+
E[int, str, float],
5938+
Annotated[Tuple[int, str, float], dec]
5939+
)
5940+
self.assertEqual(
5941+
E[int, str, bool, float],
5942+
Annotated[Tuple[int, str, bool, float], dec]
5943+
)
5944+
with self.assertRaises(TypeError):
5945+
E[int]
5946+
58765947
def test_annotated_in_other_types(self):
58775948
X = List[Annotated[T, 5]]
58785949
self.assertEqual(X[int], List[Annotated[int, 5]])

Lib/typing.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,6 +2080,17 @@ class Annotated:
20802080
20812081
OptimizedList = Annotated[List[T], runtime.Optimize()]
20822082
OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
2083+
2084+
- Annotated cannot be used with an unpacked TypeVarTuple::
2085+
2086+
Annotated[*Ts, Ann1] # NOT valid
2087+
2088+
This would be equivalent to
2089+
2090+
Annotated[T1, T2, T3, ..., Ann1]
2091+
2092+
where T1, T2 etc. are TypeVars, which would be invalid, because
2093+
only one type should be passed to Annotated.
20832094
"""
20842095

20852096
__slots__ = ()
@@ -2093,6 +2104,9 @@ def __class_getitem__(cls, params):
20932104
raise TypeError("Annotated[...] should be used "
20942105
"with at least two arguments (a type and an "
20952106
"annotation).")
2107+
if _is_unpacked_typevartuple(params[0]):
2108+
raise TypeError("Annotated[...] should not be used with an "
2109+
"unpacked TypeVarTuple")
20962110
msg = "Annotated[t, ...]: t must be a type."
20972111
origin = _type_check(params[0], msg, allow_special_forms=True)
20982112
metadata = tuple(params[1:])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy