From fc2ab456bba099654cd79794869aa08f92dcf0a3 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sun, 2 Feb 2025 04:39:24 +0100 Subject: [PATCH 1/5] Prevent crash on generic NamedTuple with unresolved typevar bound --- mypy/semanal_shared.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index bdd01ef6a6f35..9d3d4b2eb7391 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -364,6 +364,9 @@ class HasPlaceholders(BoolTypeQuery): def __init__(self) -> None: super().__init__(ANY_STRATEGY) + def visit_tuple_type(self, t: TupleType, /) -> bool: + return super().visit_tuple_type(t) or t.partial_fallback.accept(self) + def visit_placeholder_type(self, t: PlaceholderType) -> bool: return True From 87cf7d57ed477309410f5f583ca782383831f10e Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sun, 2 Feb 2025 19:18:30 +0100 Subject: [PATCH 2/5] Move partial_fallback higher up inheritance chain --- mypy/semanal_shared.py | 3 --- mypy/type_visitor.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 9d3d4b2eb7391..bdd01ef6a6f35 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -364,9 +364,6 @@ class HasPlaceholders(BoolTypeQuery): def __init__(self) -> None: super().__init__(ANY_STRATEGY) - def visit_tuple_type(self, t: TupleType, /) -> bool: - return super().visit_tuple_type(t) or t.partial_fallback.accept(self) - def visit_placeholder_type(self, t: PlaceholderType) -> bool: return True diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index f62d67bc26ccb..d935b9a47a51a 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -410,7 +410,7 @@ def visit_callable_type(self, t: CallableType, /) -> T: return self.query_types(t.arg_types + [t.ret_type]) def visit_tuple_type(self, t: TupleType, /) -> T: - return self.query_types(t.items) + return self.query_types([t.partial_fallback] + t.items) def visit_typeddict_type(self, t: TypedDictType, /) -> T: return self.query_types(t.items.values()) @@ -550,7 +550,7 @@ def visit_callable_type(self, t: CallableType, /) -> bool: return args and ret def visit_tuple_type(self, t: TupleType, /) -> bool: - return self.query_types(t.items) + return self.query_types([t.partial_fallback] + t.items) def visit_typeddict_type(self, t: TypedDictType, /) -> bool: return self.query_types(list(t.items.values())) From c2c8c352a4a97f61c299b58342f3075090963ffc Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sun, 2 Feb 2025 21:43:55 +0100 Subject: [PATCH 3/5] Silence "need type annotation" from fallback --- mypy/checker.py | 5 +++++ test-data/unit/check-inference.test | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index c69b80a55fd9c..35c8832760297 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8485,6 +8485,11 @@ def visit_type_var(self, t: TypeVarType) -> bool: # multi-step type inference. return t.id.is_meta_var() + def visit_tuple_type(self, t: TupleType, /) -> bool: + # Exclude fallback to avoid bogus "need type annotation" errors + # TODO: Maybe erase plain tuples used as fallback in TupleType constructor? + return self.query_types(t.items) + class SetNothingToAny(TypeTranslator): """Replace all ambiguous Uninhabited types with Any (to avoid spurious extra errors).""" diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 0da1c092efe89..7dd07d802bb0a 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3886,3 +3886,10 @@ def a4(x: List[str], y: List[Never]) -> None: reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]" z1[1].append("asdf") # E: "object" has no attribute "append" [builtins fixtures/dict.pyi] + +[case testTupleJoinFallbackInference] +foo = [ + (1, ("a", "b")), + (2, []), +] +[builtins fixtures/tuple.pyi] From 85f78ecdc6240b1760ef1eb3c83605310a96a1ca Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sun, 2 Feb 2025 23:56:52 -0800 Subject: [PATCH 4/5] improve test --- test-data/unit/check-inference.test | 1 + 1 file changed, 1 insertion(+) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 7dd07d802bb0a..bdd0ac305904b 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3892,4 +3892,5 @@ foo = [ (1, ("a", "b")), (2, []), ] +reveal_type(foo) # N: Revealed type is "builtins.list[Tuple[builtins.int, typing.Sequence[builtins.str]]]" [builtins fixtures/tuple.pyi] From 6119136b0a1b4584c009c43e6577065b432b14c2 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 3 Feb 2025 19:42:36 +0100 Subject: [PATCH 5/5] Add a testcase --- test-data/unit/check-incremental.test | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 2cc072eb16e73..6b888c0047c37 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -6829,3 +6829,22 @@ reveal_type(a.f) tmp/b.py:4: note: Revealed type is "builtins.int" tmp/b.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "str") tmp/b.py:6: note: Revealed type is "builtins.int" + +[case testSerializeDeferredGenericNamedTuple] +import pkg +[file pkg/__init__.py] +from .lib import NT +[file pkg/lib.py] +from typing import Generic, NamedTuple, TypeVar +from pkg import does_not_exist # type: ignore +from pkg.missing import also_missing # type: ignore + +T = TypeVar("T", bound=does_not_exist) +class NT(NamedTuple, Generic[T]): + values: also_missing[T] +[file pkg/__init__.py.2] +# touch +from .lib import NT +[builtins fixtures/tuple.pyi] +[out] +[out2]