Skip to content

Commit

Permalink
New semantic analyzer: fix crash related to dataclasses.InitVar (#6984)
Browse files Browse the repository at this point in the history
Previously InitVar attributes were missing on the second semantic
analysis pass, since the dataclasses plugin removes those attributes
from the class symbol table. This caused a crash. The fix is to reset
the attribute declarations so that they will be re-added to the symbol
table on successive semantic analysis passes.

Fixes #6955.
  • Loading branch information
JukkaL authored Jun 13, 2019
1 parent 00b3a0a commit 7692f56
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 4 deletions.
20 changes: 16 additions & 4 deletions mypy/plugins/dataclasses.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Plugin that provides support for dataclasses."""

from collections import OrderedDict
from typing import Dict, List, Set, Tuple

Expand Down Expand Up @@ -166,16 +168,26 @@ def transform(self) -> None:
if decorator_arguments['frozen']:
self._freeze(attributes)

# Remove init-only vars from the class.
for attr in attributes:
if attr.is_init_var:
del info.names[attr.name]
self.reset_init_only_vars(info, attributes)

info.metadata['dataclass'] = {
'attributes': OrderedDict((attr.name, attr.serialize()) for attr in attributes),
'frozen': decorator_arguments['frozen'],
}

def reset_init_only_vars(self, info: TypeInfo, attributes: List[DataclassAttribute]) -> None:
"""Remove init-only vars from the class and reset init var declarations."""
for attr in attributes:
if attr.is_init_var:
del info.names[attr.name]
for stmt in info.defn.defs.body:
if isinstance(stmt, AssignmentStmt) and stmt.unanalyzed_type:
lvalue = stmt.lvalues[0]
if isinstance(lvalue, NameExpr) and lvalue.name == attr.name:
# Reset node so that another semantic analysis pass will
# recreate a symbol node for this attribute.
lvalue.node = None

def collect_attributes(self) -> List[DataclassAttribute]:
"""Collect all attributes declared in the dataclass and its parents.
Expand Down
20 changes: 20 additions & 0 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,26 @@ app.database_name # E: "SpecializedApplication" has no attribute "database_name

[builtins fixtures/list.pyi]

[case testDataclassesInitVarsAndDefer]
# flags: --new-semantic-analyzer
from dataclasses import InitVar, dataclass

defer: Yes

@dataclass
class Application:
name: str
database_name: InitVar[str]

reveal_type(Application) # E: Revealed type is 'def (name: builtins.str, database_name: builtins.str) -> __main__.Application'
app = Application("example", 42) # E: Argument 2 to "Application" has incompatible type "int"; expected "str"
app = Application("example", "apps")
app.name
app.database_name # E: "Application" has no attribute "database_name"

class Yes: ...
[builtins fixtures/list.pyi]

[case testDataclassFactory]
from typing import Type, TypeVar
from dataclasses import dataclass
Expand Down

0 comments on commit 7692f56

Please sign in to comment.