Local forward refs should precede global forward refs (#19000) Fixes https://github.com/python/mypy/issues/18988 This should be a minimal change to restore backwards compatibility for an edge case with forward references.
diff --git a/mypy/semanal.py b/mypy/semanal.py index a0cfdcc..a3e1132 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py
@@ -6336,6 +6336,8 @@ if node.name not in self.globals: return True global_node = self.globals[node.name] + if not self.is_textually_before_class(global_node.node): + return True return not self.is_type_like(global_node.node) return False @@ -6363,6 +6365,13 @@ else: return line_diff > 0 + def is_textually_before_class(self, node: SymbolNode | None) -> bool: + """Similar to above, but check if a node is defined before current class.""" + assert self.type is not None + if node is None: + return False + return node.line < self.type.defn.line + def is_overloaded_item(self, node: SymbolNode, statement: Statement) -> bool: """Check whether the function belongs to the overloaded variants""" if isinstance(node, OverloadedFuncDef) and isinstance(statement, FuncDef):
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index ba4104a..2244548 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test
@@ -2004,15 +2004,18 @@ [case testPEP695TypeAliasRecursiveOuterClass] class A: - type X = X + type X = X # E: Cannot resolve name "X" (possible cyclic definition) class X: ... +class AA: + XX = XX # OK, we allow this as a special case. +class XX: ... + class Y: ... class B: type Y = Y -x: A.X -reveal_type(x) # N: Revealed type is "__main__.X" +reveal_type(AA.XX) # N: Revealed type is "def () -> __main__.XX" y: B.Y reveal_type(y) # N: Revealed type is "__main__.Y" [builtins fixtures/tuple.pyi] @@ -2029,3 +2032,30 @@ 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 +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] + +[case testForwardNestedPrecedesForwardGlobal] +from typing import NewType + +class W[T]: pass + +class R: + class M(W[Action.V], type): + FOO = R.Action.V(0) + class Action(metaclass=M): + V = NewType('V', int) + +class Action: + pass