Skip to content

Commit

Permalink
Fix unbound PS or TVT in Callable
Browse files Browse the repository at this point in the history
  • Loading branch information
hamdanal committed Feb 21, 2024
1 parent 5d1da8b commit aaf19dd
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
7 changes: 6 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3842,10 +3842,15 @@ def analyze_type_alias_type_params(self, rvalue: CallExpr) -> TypeVarLikeList:
base,
code=codes.TYPE_VAR,
)
sym = self.lookup_qualified(base.name, base)
if sym and sym.fullname in ("typing.Unpack", "typing_extensions.Unpack"):
self.note(
"Don't Unpack type variables in type_params", base, code=codes.TYPE_VAR
)
continue
if tvar in declared_tvars:
self.fail(
"Duplicate type variables in type_params argument to TypeAliasType",
f'Duplicate type variable "{tvar[0]}" in type_params argument to TypeAliasType',
base,
code=codes.TYPE_VAR,
)
Expand Down
27 changes: 25 additions & 2 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
if tvar_def is None:
if self.allow_unbound_tvars:
return t
if self.has_type_params:
if self.defining_alias and self.has_type_params:
msg = f'ParamSpec "{t.name}" is not included in type_params'
else:
msg = f'ParamSpec "{t.name}" is unbound'
Expand Down Expand Up @@ -386,7 +386,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
if tvar_def is None:
if self.allow_unbound_tvars:
return t
if self.has_type_params:
if self.defining_alias and self.has_type_params:
msg = f'TypeVarTuple "{t.name}" is not included in type_params'
else:
msg = f'TypeVarTuple "{t.name}" is unbound'
Expand Down Expand Up @@ -1258,6 +1258,19 @@ def analyze_callable_args_for_paramspec(
AnyType(TypeOfAny.explicit), ret_type=ret_type, fallback=fallback
)
return None
elif (
self.defining_alias
and self.has_type_params
and tvar_def not in self.allowed_alias_tvars
):
self.fail(
f'ParamSpec "{callable_args.name}" is not included in type_params',
callable_args,
code=codes.VALID_TYPE,
)
return callable_with_ellipsis(
AnyType(TypeOfAny.special_form), ret_type=ret_type, fallback=fallback
)

return CallableType(
[
Expand Down Expand Up @@ -1439,6 +1452,16 @@ def analyze_callable_args(
and self.refers_to_full_names(arg, ("typing_extensions.Unpack", "typing.Unpack"))
or isinstance(arg, UnpackType)
):
if self.defining_alias and self.has_type_params:
tvar_likes = self.find_type_var_likes(arg)
for name, tvar_expr in tvar_likes:
if (name, tvar_expr) not in self.allowed_alias_tvars:
self.fail(
f'Type variable "{name}" is not included in type_params',
arglist,
code=codes.VALID_TYPE,
)
return None
if seen_unpack:
# Multiple unpacks, preserve them, so we can give an error later.
if i == len(arglist.items) - 1 and not invalid_unpacks:
Expand Down
45 changes: 40 additions & 5 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,8 @@ Ts = TypeVarTuple("Ts")
Ts1 = TypeVarTuple("Ts1")
P = ParamSpec("P")

Ta0 = TypeAliasType("Ta0", int, type_params=(T, T)) # E: Duplicate type variable "T" in type_params argument to TypeAliasType

Ta1 = TypeAliasType("Ta1", int, type_params=K) # E: Tuple literal expected as the type_params argument to TypeAliasType

Ta2 = TypeAliasType("Ta2", int, type_params=(None,)) # E: Free type variable expected in type_params argument to TypeAliasType
Expand Down Expand Up @@ -1170,20 +1172,53 @@ unbound_ps_alias: Ta7[[int], str] # E: Bracketed expression "[...]" is not vali
# E: Bad number of arguments for type alias, expected 0, given 2
reveal_type(unbound_ps_alias) # N: Revealed type is "__main__.G[Any, Any]"

# TODO this does not work yet, it should report unbound P on the next line
# Ta8 = TypeAliasType("Ta8", Callable[P, int])
# unbound_ps_alias2: Ta8[int]
# reveal_type(unbound_ps_alias2)
Ta8 = TypeAliasType("Ta8", Callable[P, int]) # E: ParamSpec "P" is not included in type_params
unbound_ps_alias2: Ta8[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(unbound_ps_alias2) # N: Revealed type is "def [P] (*Any, **Any) -> builtins.int"

Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included in type_params \
# E: Type variable "T" is not included in type_params
unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2
reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any"

Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) # E: Type variable "Ts" is not included in type_params
unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(unbound_tvt_alias2) # N: Revealed type is "Any"

[builtins fixtures/dict.pyi]

[case testTypeAliasType312]
# flags: --python-version 3.12
from typing import Union, TypeAliasType
from typing import Callable, TypeAliasType, TypeVar, TypeVarTuple

T = TypeVar("T")
Ts = TypeVarTuple("Ts")

TestType = TypeAliasType("TestType", int | str)
x: TestType = 42
y: TestType = 'a'
z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]")

BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is not included in type_params
ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]"

BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) # E: Type variable "Ts" is not included in type_params
ba2: BadAlias2[int] # E: Bad number of arguments for type alias, expected 0, given 1
reveal_type(ba2) # N: Revealed type is "Any"

[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testTypeAliasTypeNoUnpackInTypeParams311]
# flags: --python-version 3.11
from typing_extensions import TypeAliasType, TypeVar, TypeVarTuple, Unpack

T = TypeVar("T")
Ts = TypeVarTuple("Ts")

Ta1 = TypeAliasType("Ta1", None, type_params=(*Ts,)) # E: can't use starred expression here
Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type variable expected in type_params argument to TypeAliasType \
# N: Don't Unpack type variables in type_params

[builtins fixtures/tuple.pyi]

0 comments on commit aaf19dd

Please sign in to comment.