Skip to content

Commit 87ec5b6

Browse files
committed
Allow TypeVarTuple arguments to subclasses of generic TypedDict
1 parent fb16e93 commit 87ec5b6

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

mypy/semanal_shared.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def anal_type(
183183
allow_tuple_literal: bool = False,
184184
allow_unbound_tvars: bool = False,
185185
allow_typed_dict_special_forms: bool = False,
186+
allow_unpack: bool = False,
186187
allow_placeholder: bool = False,
187188
report_invalid_types: bool = True,
188189
prohibit_self_type: str | None = None,

mypy/semanal_typeddict.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,16 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
253253

254254
for arg_expr in args:
255255
try:
256-
type = expr_to_unanalyzed_type(arg_expr, self.options, self.api.is_stub_file)
256+
type = expr_to_unanalyzed_type(
257+
arg_expr, self.options, self.api.is_stub_file, allow_unpack=True
258+
)
257259
except TypeTranslationError:
258260
self.fail("Invalid TypedDict type argument", ctx)
259261
return None
260262
analyzed = self.api.anal_type(
261263
type,
262264
allow_typed_dict_special_forms=True,
265+
allow_unpack=True,
263266
allow_placeholder=not self.api.is_func_scope(),
264267
)
265268
if analyzed is None:

test-data/unit/check-typevar-tuple.test

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,116 @@ td2 = A({"fn": bad, "val": 42}) # E: Incompatible types (expression has type "C
11581158
[builtins fixtures/dict.pyi]
11591159
[typing fixtures/typing-typeddict.pyi]
11601160

1161+
[case testVariadicTypedDictDeriveUnpack]
1162+
from typing import Tuple, Callable, Generic, TypedDict, TypeVar
1163+
from typing_extensions import TypeVarTuple, Unpack
1164+
1165+
T = TypeVar("T")
1166+
Ts = TypeVarTuple("Ts")
1167+
class A(TypedDict, Generic[Unpack[Ts]]):
1168+
fn: Callable[[Unpack[Ts]], None]
1169+
1170+
class B(A[Unpack[Ts]]):
1171+
val: bool
1172+
1173+
class C(A[Unpack[Ts]], Generic[Unpack[Ts], T]):
1174+
val: T
1175+
1176+
1177+
y: B[int, str]
1178+
reveal_type(y) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1179+
reveal_type(y["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1180+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1181+
1182+
y2: C[int, str, bool]
1183+
reveal_type(y2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1184+
reveal_type(y2["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1185+
reveal_type(y2["val"]) # N: Revealed type is "builtins.bool"
1186+
1187+
z: B[Unpack[Tuple[int, ...]]]
1188+
reveal_type(z) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (*builtins.int), 'val': builtins.bool})"
1189+
reveal_type(z["fn"]) # N: Revealed type is "def (*builtins.int)"
1190+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1191+
1192+
z2: C[Unpack[Tuple[int, ...]]]
1193+
reveal_type(z2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.int})"
1194+
reveal_type(z2["fn"]) # N: Revealed type is "def (*builtins.int)"
1195+
reveal_type(z2["val"]) # N: Revealed type is "builtins.int"
1196+
1197+
z3: C[Unpack[Tuple[int, ...]], bool]
1198+
reveal_type(z3) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.bool})"
1199+
reveal_type(z3["fn"]) # N: Revealed type is "def (*builtins.int)"
1200+
reveal_type(z3["val"]) # N: Revealed type is "builtins.bool"
1201+
1202+
t: B[int, Unpack[Tuple[int, str]]]
1203+
reveal_type(t) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.int, builtins.str), 'val': builtins.bool})"
1204+
1205+
t2: C[int, Unpack[Tuple[int, str]]]
1206+
reveal_type(t2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.int), 'val': builtins.str})"
1207+
1208+
def test(x: int, y: str) -> None: ...
1209+
td = B({"fn": test, "val": False})
1210+
reveal_type(td) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1211+
td2 = C({"fn": test, "val": False})
1212+
reveal_type(td2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1213+
1214+
def bad() -> int: ...
1215+
td3 = B({"fn": bad, "val": False}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]")
1216+
td4 = C({"fn": bad, "val": False}) # E: Incompatible types (expression has type "Callable[[], int]", TypedDict item "fn" has type "Callable[[], None]")
1217+
1218+
[builtins fixtures/dict.pyi]
1219+
[typing fixtures/typing-typeddict.pyi]
1220+
1221+
[case testVariadicTypedDictDeriveStar]
1222+
from typing import Tuple, Callable, Generic, TypedDict, TypeVar
1223+
from typing_extensions import TypeVarTuple
1224+
1225+
T = TypeVar("T")
1226+
Ts = TypeVarTuple("Ts")
1227+
class A(TypedDict, Generic[*Ts]):
1228+
fn: Callable[[*Ts], None]
1229+
1230+
class B(A[*Ts]):
1231+
val: bool
1232+
1233+
class C(A[*Ts], Generic[*Ts, T]):
1234+
val: T
1235+
1236+
1237+
y: B[int, str]
1238+
reveal_type(y) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1239+
reveal_type(y["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1240+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1241+
1242+
y2: C[int, str, bool]
1243+
reveal_type(y2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.str), 'val': builtins.bool})"
1244+
reveal_type(y2["fn"]) # N: Revealed type is "def (builtins.int, builtins.str)"
1245+
reveal_type(y2["val"]) # N: Revealed type is "builtins.bool"
1246+
1247+
z: B[*Tuple[int, ...]]
1248+
reveal_type(z) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (*builtins.int), 'val': builtins.bool})"
1249+
reveal_type(z["fn"]) # N: Revealed type is "def (*builtins.int)"
1250+
reveal_type(y["val"]) # N: Revealed type is "builtins.bool"
1251+
1252+
z2: C[*Tuple[int, ...]]
1253+
reveal_type(z2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.int})"
1254+
reveal_type(z2["fn"]) # N: Revealed type is "def (*builtins.int)"
1255+
reveal_type(z2["val"]) # N: Revealed type is "builtins.int"
1256+
1257+
z3: C[*Tuple[int, ...], bool]
1258+
reveal_type(z3) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (*builtins.int), 'val': builtins.bool})"
1259+
reveal_type(z3["fn"]) # N: Revealed type is "def (*builtins.int)"
1260+
reveal_type(z3["val"]) # N: Revealed type is "builtins.bool"
1261+
1262+
t: B[int, *Tuple[int, str]]
1263+
reveal_type(t) # N: Revealed type is "TypedDict('__main__.B', {'fn': def (builtins.int, builtins.int, builtins.str), 'val': builtins.bool})"
1264+
1265+
t2: C[int, *Tuple[int, str]]
1266+
reveal_type(t2) # N: Revealed type is "TypedDict('__main__.C', {'fn': def (builtins.int, builtins.int), 'val': builtins.str})"
1267+
1268+
[builtins fixtures/dict.pyi]
1269+
[typing fixtures/typing-typeddict.pyi]
1270+
11611271
[case testFixedUnpackWithRegularInstance]
11621272
from typing import Tuple, Generic, TypeVar
11631273
from typing_extensions import Unpack

0 commit comments

Comments
 (0)