summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMatthew Rahtz <matthew.rahtz@gmail.com>2022-04-16 04:24:28 (GMT)
committerGitHub <noreply@github.com>2022-04-16 04:24:28 (GMT)
commitf2bc12f0d5297899b57f3fa688b24f3c1d1bee7b (patch)
treed0d8734dd9ab6258a8997b809fa05cb60d9f9c4d /Lib
parent468314cc8bfdb6fd328cbbbb7d0807728f25e043 (diff)
downloadcpython-f2bc12f0d5297899b57f3fa688b24f3c1d1bee7b.zip
cpython-f2bc12f0d5297899b57f3fa688b24f3c1d1bee7b.tar.gz
cpython-f2bc12f0d5297899b57f3fa688b24f3c1d1bee7b.tar.bz2
bpo-43224: Add tests for TypeVarTuple substitution in Annotated (GH-31846)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_typing.py71
-rw-r--r--Lib/typing.py14
2 files changed, 85 insertions, 0 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 97fc66a..ffd0592 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -5873,6 +5873,77 @@ class AnnotatedTests(BaseTestCase):
with self.assertRaises(TypeError):
LI[None]
+ def test_typevar_subst(self):
+ dec = "a decoration"
+ Ts = TypeVarTuple('Ts')
+ T = TypeVar('T')
+ T1 = TypeVar('T1')
+ T2 = TypeVar('T2')
+
+ A = Annotated[Tuple[Unpack[Ts]], dec]
+ self.assertEqual(A[int], Annotated[Tuple[int], dec])
+ self.assertEqual(A[str, int], Annotated[Tuple[str, int], dec])
+ with self.assertRaises(TypeError):
+ Annotated[Unpack[Ts], dec]
+
+ B = Annotated[Tuple[T, Unpack[Ts]], dec]
+ self.assertEqual(B[int], Annotated[Tuple[int], dec])
+ self.assertEqual(B[int, str], Annotated[Tuple[int, str], dec])
+ self.assertEqual(
+ B[int, str, float],
+ Annotated[Tuple[int, str, float], dec]
+ )
+ with self.assertRaises(TypeError):
+ B[()]
+
+ C = Annotated[Tuple[Unpack[Ts], T], dec]
+ self.assertEqual(C[int], Annotated[Tuple[int], dec])
+ self.assertEqual(C[int, str], Annotated[Tuple[int, str], dec])
+ self.assertEqual(
+ C[int, str, float],
+ Annotated[Tuple[int, str, float], dec]
+ )
+ with self.assertRaises(TypeError):
+ C[()]
+
+ D = Annotated[Tuple[T1, Unpack[Ts], T2], dec]
+ self.assertEqual(D[int, str], Annotated[Tuple[int, str], dec])
+ self.assertEqual(
+ D[int, str, float],
+ Annotated[Tuple[int, str, float], dec]
+ )
+ self.assertEqual(
+ D[int, str, bool, float],
+ Annotated[Tuple[int, str, bool, float], dec]
+ )
+ with self.assertRaises(TypeError):
+ D[int]
+
+ # Now let's try creating an alias from an alias.
+
+ Ts2 = TypeVarTuple('Ts2')
+ T3 = TypeVar('T3')
+ T4 = TypeVar('T4')
+
+ E = D[T3, Unpack[Ts2], T4]
+ self.assertEqual(
+ E,
+ Annotated[Tuple[T3, Unpack[Ts2], T4], dec]
+ )
+ self.assertEqual(
+ E[int, str], Annotated[Tuple[int, str], dec]
+ )
+ self.assertEqual(
+ E[int, str, float],
+ Annotated[Tuple[int, str, float], dec]
+ )
+ self.assertEqual(
+ E[int, str, bool, float],
+ Annotated[Tuple[int, str, bool, float], dec]
+ )
+ with self.assertRaises(TypeError):
+ E[int]
+
def test_annotated_in_other_types(self):
X = List[Annotated[T, 5]]
self.assertEqual(X[int], List[Annotated[int, 5]])
diff --git a/Lib/typing.py b/Lib/typing.py
index 1b584be..b26adc6 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2080,6 +2080,17 @@ class Annotated:
OptimizedList = Annotated[List[T], runtime.Optimize()]
OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+
+ - Annotated cannot be used with an unpacked TypeVarTuple::
+
+ Annotated[*Ts, Ann1] # NOT valid
+
+ This would be equivalent to
+
+ Annotated[T1, T2, T3, ..., Ann1]
+
+ where T1, T2 etc. are TypeVars, which would be invalid, because
+ only one type should be passed to Annotated.
"""
__slots__ = ()
@@ -2093,6 +2104,9 @@ class Annotated:
raise TypeError("Annotated[...] should be used "
"with at least two arguments (a type and an "
"annotation).")
+ if _is_unpacked_typevartuple(params[0]):
+ raise TypeError("Annotated[...] should not be used with an "
+ "unpacked TypeVarTuple")
msg = "Annotated[t, ...]: t must be a type."
origin = _type_check(params[0], msg, allow_special_forms=True)
metadata = tuple(params[1:])