From a0697410c95542b1e9e9528524edbdb27d661dda Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 31 Mar 2025 01:00:03 +0200 Subject: [PATCH 1/4] Show correct output line --- mypy/typeanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 9208630937e7..7bf21709b863 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -2006,7 +2006,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 Unpack in a type is not allowed", final_unpack) + self.fail("More than one Unpack in a type is not allowed", final_unpack.type) return new_items def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType: From a63b5c9fd7db0c802e5db7f83221ad370b69566e Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 31 Mar 2025 01:02:17 +0200 Subject: [PATCH 2/4] Check and fix unpacks early - normally does that, but semanal uses its own anal_type wrapper in a loop --- mypy/semanal.py | 3 +++ test-data/unit/check-python312.test | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index a8a698c046f3..406913a2a59d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6071,6 +6071,9 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: return None types.append(analyzed) + analyzer = self.type_analyzer() + types = analyzer.check_unpacks_in_list(types) + if has_param_spec and num_args == 1 and types: first_arg = get_proper_type(types[0]) single_any = len(types) == 1 and isinstance(first_arg, AnyType) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index ba4104a50048..0ebecd1e21d6 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2029,3 +2029,13 @@ def foo() -> None: class Z: ... # E: Name "Z" already defined on line 2 [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] + +[case testPEP695MultipleUnpacksInBareApplicationNoCrash] +# https://github.com/python/mypy/issues/18856 +class A[*Ts]: ... + +A[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed + +b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] From 7217f8b7bd913e38339ce918ddc415215e53da32 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 31 Mar 2025 01:07:25 +0200 Subject: [PATCH 3/4] Add more variations to the testcase --- test-data/unit/check-python312.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 0ebecd1e21d6..2f3d5e08dab3 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2035,7 +2035,10 @@ class Z: ... # E: Name "Z" already defined on line 2 class A[*Ts]: ... A[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed +a: A[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed +def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ... # E: More than one Unpack in a type is not allowed +tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] From 6e8eac18acb4c9f6127b8f86dd0f8d063a7c6a05 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 31 Mar 2025 01:04:15 +0100 Subject: [PATCH 4/4] Update mypy/semanal.py --- mypy/semanal.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 406913a2a59d..6aa5977c110f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6071,9 +6071,8 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None: return None types.append(analyzed) - analyzer = self.type_analyzer() - types = analyzer.check_unpacks_in_list(types) - + if allow_unpack: + types = self.type_analyzer().check_unpacks_in_list(types) if has_param_spec and num_args == 1 and types: first_arg = get_proper_type(types[0]) single_any = len(types) == 1 and isinstance(first_arg, AnyType)