Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mypy/exprtotype.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ def expr_to_unanalyzed_type(
expr.expr, options, allow_new_syntax, lookup_qualified=lookup_qualified
),
from_star_syntax=True,
line=expr.line,
column=expr.column,
)
elif isinstance(expr, DictExpr):
if not expr.items:
Expand Down
52 changes: 23 additions & 29 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2338,17 +2338,10 @@ class Foo(Bar, Generic[T]): ...
removed: list[int] = []
declared_tvars: TypeVarLikeList = []
is_protocol = False
has_type_var_tuple = False
if defn.type_args is not None:
for p in defn.type_args:
node = self.get_defined_type_param(p.name)
assert node is not None
assert isinstance(node, TypeVarLikeExpr)
if isinstance(node, TypeVarTupleExpr):
if has_type_var_tuple:
self.fail("Can only use one type var tuple in a class def", context)
continue
has_type_var_tuple = True
declared_tvars.append((p.name, node))

for i, base_expr in enumerate(base_type_exprs):
Expand All @@ -2361,7 +2354,7 @@ class Foo(Bar, Generic[T]): ...
except TypeTranslationError:
# This error will be caught later.
continue
result = self.analyze_class_typevar_declaration(base, has_type_var_tuple)
result = self.analyze_class_typevar_declaration(base)
if result is not None:
tvars = result[0]
is_protocol |= result[1]
Expand Down Expand Up @@ -2412,12 +2405,11 @@ class Foo(Bar, Generic[T]): ...
# grained incremental mode.
defn.removed_base_type_exprs.append(defn.base_type_exprs[i])
del base_type_exprs[i]
declared_tvars = self.pre_validate_declared_tvars(declared_tvars, context, "class")
tvar_defs = self.tvar_defs_from_tvars(declared_tvars, context)
return base_type_exprs, tvar_defs, is_protocol, declared_tvars

def analyze_class_typevar_declaration(
self, base: Type, has_type_var_tuple: bool
) -> tuple[TypeVarLikeList, bool] | None:
def analyze_class_typevar_declaration(self, base: Type) -> tuple[TypeVarLikeList, bool] | None:
"""Analyze type variables declared using Generic[...] or Protocol[...].

Args:
Expand All @@ -2443,11 +2435,6 @@ def analyze_class_typevar_declaration(
tag = self.track_incomplete_refs()
tvar = self.analyze_unbound_tvar(arg)
if tvar:
if isinstance(tvar[1], TypeVarTupleExpr):
if has_type_var_tuple:
self.fail("Can only use one type var tuple in a class def", base)
continue
has_type_var_tuple = True
tvars.append(tvar)
elif not self.found_incomplete_ref(tag):
self.fail("Free type variable expected in %s[...]" % sym.node.name, base)
Expand Down Expand Up @@ -4083,6 +4070,24 @@ def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Typ
return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ))
return typ

def pre_validate_declared_tvars(
self, declared_tvars: TypeVarLikeList, ctx: Context, kind: str
) -> TypeVarLikeList:
has_type_var_tuple = False
valid_declared_tvars = []
for name, node in declared_tvars:
if isinstance(node, TypeVarTupleExpr):
if has_type_var_tuple:
self.fail(
f"Can only use one TypeVarTuple in a {kind} definition",
ctx,
code=codes.TYPE_VAR,
)
continue
has_type_var_tuple = True
valid_declared_tvars.append((name, node))
return valid_declared_tvars

def analyze_alias(
self,
name: str,
Expand Down Expand Up @@ -4119,6 +4124,7 @@ def analyze_alias(
found_type_vars = self.find_type_var_likes(typ)
namespace = self.qualified_name(name)
alias_type_vars = found_type_vars if declared_type_vars is None else declared_type_vars
alias_type_vars = self.pre_validate_declared_tvars(alias_type_vars, rvalue, "type alias")
with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)):
tvar_defs = self.tvar_defs_from_tvars(alias_type_vars, typ)

Expand Down Expand Up @@ -4426,7 +4432,6 @@ def analyze_type_alias_type_params(

declared_tvars: TypeVarLikeList = []
all_declared_tvar_names: list[str] = [] # includes bound type variables
have_type_var_tuple = False
for tp_expr in type_params:
if isinstance(tp_expr, StarExpr):
tp_expr.valid = False
Expand All @@ -4440,18 +4445,7 @@ def analyze_type_alias_type_params(

tag = self.track_incomplete_refs()
tvar = self.analyze_unbound_tvar_impl(base, is_typealias_param=True)
if tvar:
if isinstance(tvar[1], TypeVarTupleExpr):
if have_type_var_tuple:
self.fail(
"Can only use one TypeVarTuple in type_params argument to TypeAliasType",
base,
code=codes.TYPE_VAR,
)
have_type_var_tuple = True
continue
have_type_var_tuple = True
elif not self.found_incomplete_ref(tag):
if not tvar and not self.found_incomplete_ref(tag):
sym = self.lookup_qualified(base.name, base)
if sym and isinstance(sym.node, TypeVarLikeExpr):
all_declared_tvar_names.append(sym.node.name) # Error will be reported later
Expand Down
9 changes: 7 additions & 2 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1180,7 +1180,12 @@ def visit_unpack_type(self, t: UnpackType) -> Type:
self.fail(message_registry.INVALID_UNPACK_POSITION, t.type, code=codes.VALID_TYPE)
return AnyType(TypeOfAny.from_error)
self.allow_type_var_tuple = self.nesting_level + 1
result = UnpackType(self.anal_type(t.type), from_star_syntax=t.from_star_syntax)
result = UnpackType(
self.anal_type(t.type),
from_star_syntax=t.from_star_syntax,
line=t.line,
column=t.column,
)
self.allow_type_var_tuple = -1
return result

Expand Down Expand Up @@ -2078,7 +2083,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]:

if num_unpacks > 1:
assert final_unpack is not None
self.fail("More than one variadic Unpack in a type is not allowed", final_unpack.type)
self.fail("More than one variadic Unpack in a type is not allowed", final_unpack)
return new_items

def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType:
Expand Down
29 changes: 27 additions & 2 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -2288,11 +2288,11 @@ from typing_extensions import TypeVarTuple, Unpack

Us = TypeVarTuple("Us")

class C[*Ts, *Us]: # E: Can only use one type var tuple in a class def
class C[*Ts, *Us]: # E: Can only use one TypeVarTuple in a class definition
pass

class D[*Ts](Generic[Unpack[Us]]): # E: Generic[...] base class is redundant \
# E: Can only use one type var tuple in a class def
# E: Can only use one TypeVarTuple in a class definition
pass
[builtins fixtures/tuple.pyi]

Expand Down Expand Up @@ -2438,3 +2438,28 @@ def test[*Us](*xs: *Us) -> None:
# TODO: we should erase last_known_values from Parameters.
reveal_type(print_args(2, *xs, 4)) # N: Revealed type is "__main__.C[[Literal[2]?, *args: *tuple[*Us, Literal[4]?]], tuple[Literal[2]?, *Us, Literal[4]?]]"
[builtins fixtures/paramspec.pyi]

[case testDoubleTypeVarTupleNoCrashNewSyntax]
class C[*T, *U](T, U): # E: Can only use one TypeVarTuple in a class definition \
# E: TypeVarTuple "T" is only valid with an unpack \
# E: TypeVarTuple "U" is unbound
x: tuple[*T]
y: tuple[*T, *U] # E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "U" is unbound
z: tuple[*U] # E: TypeVarTuple "U" is unbound

type A[*T, *U] = tuple[tuple[*T], tuple[*T, *U], tuple[*U]] # E: Can only use one TypeVarTuple in a type alias definition \
# E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "U" is unbound

c: C[int, str]
a: A[int, str]

reveal_type(c.x) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(c.y) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(c.z) # N: Revealed type is "builtins.tuple[Any, ...]"

reveal_type(a[0]) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(a[1]) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(a[2]) # N: Revealed type is "builtins.tuple[Any, ...]"
[builtins fixtures/tuple.pyi]
2 changes: 1 addition & 1 deletion test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ partially_generic1: Ta3[int] = {"a": 1}
reveal_type(partially_generic1) # N: Revealed type is "builtins.dict[Any, builtins.int]"
partially_generic2: Ta3[int] = {1: "a"} # E: Dict entry 0 has incompatible type "int": "str"; expected "Any": "int"

Ta4 = TypeAliasType("Ta4", Tuple[Unpack[Ts]], type_params=(Ts, Ts1)) # E: Can only use one TypeVarTuple in type_params argument to TypeAliasType
Ta4 = TypeAliasType("Ta4", Tuple[Unpack[Ts]], type_params=(Ts, Ts1)) # E: Can only use one TypeVarTuple in a type alias definition

Ta5 = TypeAliasType("Ta5", Dict) # Unlike old style aliases, this is not generic
non_generic_dict: Ta5[int, str] # E: Bad number of arguments for type alias, expected 0, given 2
Expand Down
42 changes: 39 additions & 3 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -725,15 +725,21 @@ Ts = TypeVarTuple("Ts")
Us = TypeVarTuple("Us")
class G(Generic[Unpack[Ts]]): ...

A = Tuple[Unpack[Ts], Unpack[Us]] # E: More than one variadic Unpack in a type is not allowed
A = Tuple[Unpack[Ts], Unpack[Us]] # E: Can only use one TypeVarTuple in a type alias definition \
# E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "Us" is unbound
x: A[int, str]
reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str]"

B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: More than one variadic Unpack in a type is not allowed
B = Callable[[Unpack[Ts], Unpack[Us]], int] # E: Can only use one TypeVarTuple in a type alias definition \
# E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "Us" is unbound
y: B[int, str]
reveal_type(y) # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int"

C = G[Unpack[Ts], Unpack[Us]] # E: More than one variadic Unpack in a type is not allowed
C = G[Unpack[Ts], Unpack[Us]] # E: Can only use one TypeVarTuple in a type alias definition \
# E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "Us" is unbound
z: C[int, str]
reveal_type(z) # N: Revealed type is "__main__.G[builtins.int, builtins.str]"
[builtins fixtures/tuple.pyi]
Expand Down Expand Up @@ -3273,3 +3279,33 @@ reveal_type(x) # N: Revealed type is "__main__.C[builtins.int, builtins.int]"
y: Union[Any, C[Unpack[tuple[int, ...]]]]
reveal_type(bar(y)) # N: Revealed type is "builtins.tuple[Any, ...]"
[builtins fixtures/tuple.pyi]

[case testDoubleTypeVarTupleNoCrashOldSyntax]
from typing import Generic, TypeVarTuple, Unpack

T = TypeVarTuple("T")
U = TypeVarTuple("U")

class C(T, U, Generic[Unpack[T], Unpack[U]]): # E: Can only use one TypeVarTuple in a class definition \
# E: TypeVarTuple "T" is only valid with an unpack \
# E: TypeVarTuple "U" is unbound
x: tuple[Unpack[T]]
y: tuple[Unpack[T], Unpack[U]] # E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "U" is unbound
z: tuple[Unpack[U]] # E: TypeVarTuple "U" is unbound

A = tuple[tuple[Unpack[T]], tuple[Unpack[T], Unpack[U]], tuple[Unpack[U]]] # E: Can only use one TypeVarTuple in a type alias definition \
# E: More than one variadic Unpack in a type is not allowed \
# E: TypeVarTuple "U" is unbound

c: C[int, str]
a: A[int, str]

reveal_type(c.x) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(c.y) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(c.z) # N: Revealed type is "builtins.tuple[Any, ...]"

reveal_type(a[0]) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(a[1]) # N: Revealed type is "tuple[builtins.int, builtins.str]"
reveal_type(a[2]) # N: Revealed type is "builtins.tuple[Any, ...]"
[builtins fixtures/tuple.pyi]
2 changes: 1 addition & 1 deletion test-data/unit/semanal-errors.test
Original file line number Diff line number Diff line change
Expand Up @@ -1502,7 +1502,7 @@ x: TVariadic # E: TypeVarTuple "TVariadic" is unbound
y: Unpack[TVariadic] # E: Unpack is only valid in a variadic position


class Variadic(Generic[Unpack[TVariadic], Unpack[TVariadic2]]): # E: Can only use one type var tuple in a class def
class Variadic(Generic[Unpack[TVariadic], Unpack[TVariadic2]]): # E: Can only use one TypeVarTuple in a class definition
pass

def bad_args(*args: TVariadic): # E: TypeVarTuple "TVariadic" is only valid with an unpack
Expand Down
Loading