From 88fec70b1bba8fa539d955932b62ddc9f6ba9ac4 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 22 Nov 2023 15:48:51 +0000 Subject: [PATCH 1/2] Fix crash with type alias to `Callable[[Unpack[Tuple[Any, ...]]], Any]` --- mypy/expandtype.py | 8 +++-- test-data/unit/check-typevar-tuple.test | 42 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 3acec4b96d06..958cacf38d5a 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -307,15 +307,19 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) - # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] if isinstance(var_arg_type, TupleType): + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] expanded_tuple = var_arg_type.accept(self) assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) expanded_items = expanded_tuple.items fallback = var_arg_type.partial_fallback + elif isinstance(var_arg_type, Instance): + # We have something like Unpack[Tuple[Any, ...]] + expanded_items = list(var_arg_type.args) + fallback = var_arg_type else: # We have plain Unpack[Ts] - assert isinstance(var_arg_type, TypeVarTupleType) + assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) fallback = var_arg_type.tuple_fallback expanded_items = self.expand_unpack(var_arg) new_unpack = UnpackType(TupleType(expanded_items, fallback)) diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 487f22699724..9c8d21114d4c 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2235,3 +2235,45 @@ z: Tuple[int, Unpack[Tuple[int, ...]]] = (1,) w: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *[2, 3, 4]) t: Tuple[int, Unpack[Tuple[int, ...]]] = (1, *(2, 3, 4)) [builtins fixtures/tuple.pyi] + +[case testAliasToCallableWithUnpack] +from typing import Any, Callable, Tuple, Unpack + +_CallableValue = Callable[[Unpack[Tuple[Any, ...]]], Any] +def higher_order(f: _CallableValue) -> None: ... + +def good1(*args: int) -> None: ... +def good2(*args: str) -> int: ... + +def bad1(a: str, b: int, /) -> None: ... +def bad2(c: bytes, *args: int) -> str: ... +def bad3(*, d: str) -> int: ... +def bad4(**kwargs: None) -> None: ... + +higher_order(good1) +higher_order(good2) + +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[VarArg(Any)], Any]" +higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[VarArg(Any)], Any]" +[builtins fixtures/tuple.pyi] + +[case testAliasToCallableWithUnpack2] +from typing import Any, Callable, Tuple, Unpack + +_CallableValue = Callable[[int, str, Unpack[Tuple[Any, ...]], int], Any] +def higher_order(f: _CallableValue) -> None: ... + +def good(a: int, b: str, *args: Unpack[Tuple[Unpack[Tuple[Any, ...]], int]]) -> int: ... +def bad1(a: str, b: int, /) -> None: ... +def bad2(c: bytes, *args: int) -> str: ... +def bad3(*, d: str) -> int: ... +def bad4(**kwargs: None) -> None: ... + +higher_order(good) +higher_order(bad1) # E: Argument 1 to "higher_order" has incompatible type "Callable[[str, int], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad2) # E: Argument 1 to "higher_order" has incompatible type "Callable[[bytes, VarArg(int)], str]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Callable[[NamedArg(str, 'd')], int]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" +[builtins fixtures/tuple.pyi] From e4d02a475c5909b9b805dbe7eb80e9f063f18f64 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Thu, 7 Dec 2023 17:37:30 +0000 Subject: [PATCH 2/2] Address review --- mypy/expandtype.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 958cacf38d5a..f6aa74add9d8 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -307,22 +307,24 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) - if isinstance(var_arg_type, TupleType): - # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] - expanded_tuple = var_arg_type.accept(self) - assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) - expanded_items = expanded_tuple.items - fallback = var_arg_type.partial_fallback - elif isinstance(var_arg_type, Instance): - # We have something like Unpack[Tuple[Any, ...]] - expanded_items = list(var_arg_type.args) - fallback = var_arg_type + if isinstance(var_arg_type, Instance): + # we have something like Unpack[Tuple[Any, ...]] + new_unpack = var_arg else: - # We have plain Unpack[Ts] - assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) - fallback = var_arg_type.tuple_fallback - expanded_items = self.expand_unpack(var_arg) - new_unpack = UnpackType(TupleType(expanded_items, fallback)) + if isinstance(var_arg_type, TupleType): + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] + expanded_tuple = var_arg_type.accept(self) + assert isinstance(expanded_tuple, ProperType) and isinstance( + expanded_tuple, TupleType + ) + expanded_items = expanded_tuple.items + fallback = var_arg_type.partial_fallback + else: + # We have plain Unpack[Ts] + assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) + fallback = var_arg_type.tuple_fallback + expanded_items = self.expand_unpack(var_arg) + new_unpack = UnpackType(TupleType(expanded_items, fallback)) return prefix + [new_unpack] + suffix def visit_callable_type(self, t: CallableType) -> CallableType: