From 9b6f908c899edc1b638c20a4e0be623c5a1f330c Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 19 May 2025 03:24:43 +0200 Subject: [PATCH 1/4] Normalize tuples before calculating the fallback --- mypy/semanal_shared.py | 3 ++- test-data/unit/check-typevar-tuple.test | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index bdd01ef6a6f35..e94604b66381a 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -46,6 +46,7 @@ TypeVarLikeType, TypeVarTupleType, UnpackType, + flatten_nested_tuples, get_proper_type, ) @@ -290,7 +291,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None: fallback = typ.partial_fallback assert fallback.type.fullname == "builtins.tuple" items = [] - for item in typ.items: + for item in flatten_nested_tuples(typ.items): # TODO: this duplicates some logic in typeops.tuple_fallback(). if isinstance(item, UnpackType): unpacked_type = get_proper_type(item.type) diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index d364439f22e95..830c41a377064 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2628,3 +2628,11 @@ def fn(f: Callable[[*tuple[T]], int]) -> Callable[[*tuple[T]], int]: ... def test(*args: Unpack[tuple[T]]) -> int: ... reveal_type(fn(test)) # N: Revealed type is "def [T] (T`1) -> builtins.int" [builtins fixtures/tuple.pyi] + +[case testNoCrashSubclassingTUpleWithTrivialUnpack] +# https://github.com/python/mypy/issues/19105 +from typing import Unpack + +class A(tuple[Unpack[tuple[int]]]): ... +class B(tuple[Unpack[tuple[()]]]): ... +[builtins fixtures/tuple.pyi] From bebdf287073310cd0917b0244f27ea49c8875b5a Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 4 Jul 2025 13:58:48 +0200 Subject: [PATCH 2/4] Maybe this? --- mypy/semanal_typeargs.py | 5 +++++ test-data/unit/check-typevar-tuple.test | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 435abb78ca43b..1ef40533069f3 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -102,6 +102,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: # If there was already an error for the alias itself, there is no point in checking # the expansion, most likely it will result in the same kind of error. get_proper_type(t).accept(self) + self.visit_optional_type(t.alias) def visit_tuple_type(self, t: TupleType) -> None: t.items = flatten_nested_tuples(t.items) @@ -254,6 +255,10 @@ def visit_unpack_type(self, typ: UnpackType) -> None: def check_type_var_values( self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context ) -> bool: + if self.in_type_alias_expr: + # See testValidTypeAliasValues - we do not enforce typevar compatibility + # at the definition site. We check instantiation validity later. + return False is_error = False for actual in get_proper_types(actuals): # We skip UnboundType here, since they may appear in defn.bases, diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index b8b02e73b6854..6e4fee146603f 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2667,10 +2667,17 @@ class S(tuple[Unpack[Ts]], Generic[T, Unpack[Ts]]): def f(self, x: T, /) -> T: ... [builtins fixtures/tuple.pyi] -[case testNoCrashSubclassingTUpleWithTrivialUnpack] +[case testNoCrashSubclassingTupleWithTrivialUnpack] # https://github.com/python/mypy/issues/19105 from typing import Unpack class A(tuple[Unpack[tuple[int]]]): ... class B(tuple[Unpack[tuple[()]]]): ... + +a: A +tuple(a) +(x,) = a + +b: B +(_,) = b # E: Need more than 0 values to unpack (1 expected) [builtins fixtures/tuple.pyi] From 0aaa011f21da93ef395853773d02c1f408a3524f Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 4 Jul 2025 14:12:01 +0200 Subject: [PATCH 3/4] Fix selfcheck --- mypy/semanal_typeargs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 1ef40533069f3..be39a8259c2e4 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -102,7 +102,8 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None: # If there was already an error for the alias itself, there is no point in checking # the expansion, most likely it will result in the same kind of error. get_proper_type(t).accept(self) - self.visit_optional_type(t.alias) + if t.alias is not None: + t.alias.accept(self) def visit_tuple_type(self, t: TupleType) -> None: t.items = flatten_nested_tuples(t.items) From 8bb25fa5e39ef4f306808b55157bfeea4378b044 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 4 Jul 2025 14:13:36 +0200 Subject: [PATCH 4/4] And a test --- test-data/unit/check-typevar-tuple.test | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 6e4fee146603f..bc483cc58ad17 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2675,9 +2675,21 @@ class A(tuple[Unpack[tuple[int]]]): ... class B(tuple[Unpack[tuple[()]]]): ... a: A -tuple(a) +reveal_type(tuple(a)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" (x,) = a b: B (_,) = b # E: Need more than 0 values to unpack (1 expected) [builtins fixtures/tuple.pyi] + +[case testNoCrashSubclassingTupleWithVariadicUnpack] +# https://github.com/python/mypy/issues/19105 +from typing import Unpack + +class A(tuple[Unpack[tuple[int, ...]]]): ... + +a: A +tuple(a) +(x,) = a +(_,) = a +[builtins fixtures/tuple.pyi]