diff --git a/mypy/expandtype.py b/mypy/expandtype.py index fd507216a6be..fbd559bbd0f0 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -400,7 +400,29 @@ def expand_unpack(self, t: UnpackType) -> list[Type]: raise RuntimeError(f"Invalid type replacement to expand: {repl}") def visit_parameters(self, t: Parameters) -> Type: - return t.copy_modified(arg_types=self.expand_types(t.arg_types)) + arg_types: list[Type] = [] + arg_kinds: list[ArgKind] = [] + arg_names: list[str | None] = [] + for arg_type, arg_kind, arg_name in zip(t.arg_types, t.arg_kinds, t.arg_names): + if ( + arg_kind == ARG_STAR + and isinstance(arg_type, UnpackType) + and isinstance(arg_type.type, TypeVarTupleType) + ): + expanded = self.expand_unpack(arg_type) + for item in expanded: + arg_types.append(item) + if isinstance(item, UnpackType): + arg_kinds.append(ARG_STAR) + arg_names.append(arg_name) + else: + arg_kinds.append(ArgKind.ARG_POS) + arg_names.append(None) + else: + arg_types.append(arg_type.accept(self)) + arg_kinds.append(arg_kind) + arg_names.append(arg_name) + return t.copy_modified(arg_types=arg_types, arg_kinds=arg_kinds, arg_names=arg_names) def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> list[Type]: star_index = t.arg_kinds.index(ARG_STAR) diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index d1e928441a9e..d32c2401c6ad 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -141,6 +141,32 @@ reveal_type(whatever) # N: Revealed type is "def (x: builtins.int) -> builtins. reveal_type(whatever(217)) # N: Revealed type is "builtins.list[builtins.int]" [builtins fixtures/paramspec.pyi] +[case testParamSpecVariadicContextManager] +from typing import Callable, Generic, TypeVar, TypeVarTuple, Unpack +from typing_extensions import ParamSpec + +P = ParamSpec("P") +R = TypeVar("R") +Ts = TypeVarTuple("Ts") + +class contextmanager(Generic[P, R]): + def __init__(self, func: Callable[P, R]) -> None: ... + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> "_contextmanager_cls[P, R]": ... + +class _contextmanager_cls(Generic[P, R]): + def __enter__(self) -> R: ... + def __exit__(self, *args: object) -> bool: ... + +@contextmanager +def print_args(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]: ... + +with print_args(2, "x") as value: + reveal_type(value) # N: Revealed type is "tuple[builtins.int, builtins.str]" + +reveal_type(print_args(2, "x")) # N: Revealed type is "__main__._contextmanager_cls[[Literal[2]?, Literal['x']?], tuple[Literal[2]?, Literal['x']?]]" +[builtins fixtures/tuple.pyi] + [case testInvalidParamSpecType] from typing import ParamSpec