Skip to content

Commit 3f03755

Browse files
authored
Do not store deferred NamedTuple fields as redefinitions (#20147)
Fixes #17059. When we encounter a field with a Placeholder as a default, do not record it in the new symtable as redefinition - it becomes orphan immediately and remains there, crashing suring serialization. The test cases are both crashing on current master. Ideally we hould emit name-defined in all cases, but it is another unrelated issue (#17610, and likely some others; this is not specific to named tuples - plain classes also allow referencing variables that are not defined yet).
1 parent 99bc45f commit 3f03755

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

mypy/semanal_namedtuple.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
NamedTupleExpr,
3535
NameExpr,
3636
PassStmt,
37+
PlaceholderNode,
3738
RefExpr,
3839
Statement,
3940
StrExpr,
@@ -697,10 +698,14 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]:
697698
if isinstance(sym.node, (FuncBase, Decorator)) and not sym.plugin_generated:
698699
# Keep user-defined methods as is.
699700
continue
700-
# Keep existing (user-provided) definitions under mangled names, so they
701-
# get semantically analyzed.
702-
r_key = get_unique_redefinition_name(key, named_tuple_info.names)
703-
named_tuple_info.names[r_key] = sym
701+
# Do not retain placeholders - we'll get back here if they cease to
702+
# be placeholders later. If we keep placeholders alive, they may never
703+
# be reached again, making it to cacheable symtable.
704+
if not isinstance(sym.node, PlaceholderNode):
705+
# Keep existing (user-provided) definitions under mangled names, so they
706+
# get semantically analyzed.
707+
r_key = get_unique_redefinition_name(key, named_tuple_info.names)
708+
named_tuple_info.names[r_key] = sym
704709
named_tuple_info.names[key] = value
705710

706711
# Helpers

test-data/unit/check-namedtuple.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,3 +1530,28 @@ class Base:
15301530
names = [name for name in namespace if fail] # E: Name "fail" is not defined
15311531
self.n = namedtuple("n", names) # E: NamedTuple type as an attribute is not supported
15321532
[builtins fixtures/tuple.pyi]
1533+
1534+
[case testNamedTupleDefaultValueDefer]
1535+
# flags: --debug-serialize
1536+
from typing import NamedTuple
1537+
1538+
class NT(NamedTuple):
1539+
foo: int = UNDEFINED # E: Name "UNDEFINED" is not defined
1540+
[builtins fixtures/tuple.pyi]
1541+
1542+
[case testNamedTupleDefaultValueDefer2]
1543+
# flags: --debug-serialize
1544+
from typing import NamedTuple
1545+
1546+
class NT(NamedTuple):
1547+
foo: int = DEFERRED_INT
1548+
1549+
class NT2(NamedTuple):
1550+
foo: int = DEFERRED_STR # E: Incompatible types in assignment (expression has type "str", variable has type "int")
1551+
1552+
from foo import DEFERRED_INT, DEFERRED_STR
1553+
1554+
[file foo.py]
1555+
DEFERRED_INT = 1
1556+
DEFERRED_STR = "a"
1557+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)