Skip to content

Fix crash on dataclass field assigned a functional namedtuple() call - #21849

Open
TheMuffinMan1320 wants to merge 1 commit into
python:masterfrom
TheMuffinMan1320:fix-21583-dataclass-namedtuple-fwdref
Open

Fix crash on dataclass field assigned a functional namedtuple() call#21849
TheMuffinMan1320 wants to merge 1 commit into
python:masterfrom
TheMuffinMan1320:fix-21583-dataclass-namedtuple-fwdref

Conversation

@TheMuffinMan1320

Copy link
Copy Markdown

Fixes #21583

Root cause

from collections import namedtuple
from dataclasses import dataclass

@dataclass
class C:
    p: "Point" = namedtuple("Point", ["x", "y"])

crashes mypy with:

AssertionError: TypeInfo(
  Name(crash.C.p)
  ...

When the right-hand side of a class-body assignment is a namedtuple(...)
call, mypy's semantic analyzer (analyze_namedtuple_assign in
mypy/semanal.py) always treats the statement as a functional NamedTuple
class definition and binds the assigned name in the class's symbol table to
a TypeInfo, regardless of whether the left-hand side also carries a type
annotation. So p's symbol table entry ends up being a TypeInfo for a
synthesized Point class, not a Var, even though it looks like a normal
annotated dataclass field.

DataclassTransformer.collect_attributes() in mypy/plugins/dataclasses.py
did not anticipate this and unconditionally asserted
isinstance(node, Var) for any class-body annotated assignment that wasn't
already special-cased (TypeAlias, Decorator), causing the crash.

Fix

Skip such fields the same way TypeAlias and Decorator nodes are already
skipped in collect_attributes(), since a name bound to a TypeInfo this
way is not a valid dataclass field. This mirrors the existing handling
immediately above it in the same function and does not touch the
Var-based path used by ordinary dataclass/NamedTuple fields.

Testing

  • Added testNoCrashForDataclassFieldAssignedFunctionalNamedTuple to
    test-data/unit/check-dataclasses.test, covering both the
    name-mismatched case from the issue and a name-matched variant.
  • python -m pytest -q mypy/test/testcheck.py -k dataclass — 204 passed.
  • python -m pytest -q mypy/test/testcheck.py -k "namedtuple or NamedTuple" — 225 passed.
  • Manually confirmed the exact repro from the issue no longer crashes, and
    that normal dataclass fields with forward-referenced NamedTuple
    class types still type-check correctly.
  • pre-commit run --files mypy/plugins/dataclasses.py test-data/unit/check-dataclasses.test — all checks passed.

…ython#21583)

When a dataclass field's default value is a `namedtuple('Name', ...)`
call, the semantic analyzer treats the assignment as a NamedTuple class
definition and binds the field's name to a TypeInfo instead of a Var,
regardless of any type annotation on the left-hand side. The dataclass
plugin's collect_attributes() assumed every non-alias, non-decorator
symbol table node was a Var and asserted so, crashing when it
encountered this TypeInfo.

Skip such fields the same way TypeAlias/Decorator nodes are already
skipped, since they aren't valid dataclass fields.

Fixes python#21583
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assertion failure crash: assert isinstance(node, Var), node when dataclass has namedtuple() field

1 participant