Skip to content

Commit 149f753

Browse files
authored
Check for multiple type var tuples for PEP 695 (#20289)
Fixes #20287
1 parent 96fac3a commit 149f753

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

mypy/semanal.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,11 +2269,17 @@ class Foo(Bar, Generic[T]): ...
22692269
removed: list[int] = []
22702270
declared_tvars: TypeVarLikeList = []
22712271
is_protocol = False
2272+
has_type_var_tuple = False
22722273
if defn.type_args is not None:
22732274
for p in defn.type_args:
22742275
node = self.lookup(p.name, context)
22752276
assert node is not None
22762277
assert isinstance(node.node, TypeVarLikeExpr)
2278+
if isinstance(node.node, TypeVarTupleExpr):
2279+
if has_type_var_tuple:
2280+
self.fail("Can only use one type var tuple in a class def", context)
2281+
continue
2282+
has_type_var_tuple = True
22772283
declared_tvars.append((p.name, node.node))
22782284

22792285
for i, base_expr in enumerate(base_type_exprs):
@@ -2286,7 +2292,7 @@ class Foo(Bar, Generic[T]): ...
22862292
except TypeTranslationError:
22872293
# This error will be caught later.
22882294
continue
2289-
result = self.analyze_class_typevar_declaration(base)
2295+
result = self.analyze_class_typevar_declaration(base, has_type_var_tuple)
22902296
if result is not None:
22912297
tvars = result[0]
22922298
is_protocol |= result[1]
@@ -2340,7 +2346,9 @@ class Foo(Bar, Generic[T]): ...
23402346
tvar_defs = self.tvar_defs_from_tvars(declared_tvars, context)
23412347
return base_type_exprs, tvar_defs, is_protocol
23422348

2343-
def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList, bool] | None:
2349+
def analyze_class_typevar_declaration(
2350+
self, base: Type, has_type_var_tuple: bool
2351+
) -> tuple[TypeVarLikeList, bool] | None:
23442352
"""Analyze type variables declared using Generic[...] or Protocol[...].
23452353
23462354
Args:
@@ -2362,16 +2370,15 @@ def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList
23622370
):
23632371
is_proto = sym.node.fullname != "typing.Generic"
23642372
tvars: TypeVarLikeList = []
2365-
have_type_var_tuple = False
23662373
for arg in unbound.args:
23672374
tag = self.track_incomplete_refs()
23682375
tvar = self.analyze_unbound_tvar(arg)
23692376
if tvar:
23702377
if isinstance(tvar[1], TypeVarTupleExpr):
2371-
if have_type_var_tuple:
2378+
if has_type_var_tuple:
23722379
self.fail("Can only use one type var tuple in a class def", base)
23732380
continue
2374-
have_type_var_tuple = True
2381+
has_type_var_tuple = True
23752382
tvars.append(tvar)
23762383
elif not self.found_incomplete_ref(tag):
23772384
self.fail("Free type variable expected in %s[...]" % sym.node.name, base)

test-data/unit/check-python312.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,3 +2223,17 @@ if isinstance(x, tuple):
22232223
reveal_type(y) # N: Revealed type is "Union[typing.Hashable, Union[builtins.int, tuple[Union[typing.Hashable, ...]]]]"
22242224
[builtins fixtures/tuple.pyi]
22252225
[typing fixtures/typing-full.pyi]
2226+
2227+
[case testPEP695MultipleTypeVarTuples]
2228+
from typing import Generic
2229+
from typing_extensions import TypeVarTuple, Unpack
2230+
2231+
Us = TypeVarTuple("Us")
2232+
2233+
class C[*Ts, *Us]: # E: Can only use one type var tuple in a class def
2234+
pass
2235+
2236+
class D[*Ts](Generic[Unpack[Us]]): # E: Generic[...] base class is redundant \
2237+
# E: Can only use one type var tuple in a class def
2238+
pass
2239+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)