From 42b43445ee515ade652f4dd2bb56c29167676299 Mon Sep 17 00:00:00 2001 From: Christian Kanesan Date: Mon, 8 Apr 2019 09:21:26 +0200 Subject: [PATCH 1/2] Dataclasses: support mutable properties Skip over attributes that are redefined as properties on dataclass construction --- mypy/plugins/dataclasses.py | 7 +++++-- test-data/unit/check-dataclasses.test | 28 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index dc23b3f42454a..c565b65cd8932 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -3,7 +3,7 @@ from mypy.nodes import ( ARG_OPT, ARG_POS, MDEF, Argument, AssignmentStmt, CallExpr, - Context, Expression, FuncDef, JsonDict, NameExpr, + Context, Expression, FuncDef, JsonDict, NameExpr, OverloadedFuncDef, SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr ) from mypy.plugin import ClassDefContext @@ -204,7 +204,10 @@ def collect_attributes(self) -> List[DataclassAttribute]: continue node = cls.info.names[lhs.name].node - assert isinstance(node, Var) + assert isinstance(node, (Var, OverloadedFuncDef)) + + if isinstance(node, OverloadedFuncDef): + continue # x: ClassVar[int] is ignored by dataclasses. if node.is_classvar: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 0b094d64e6853..f4853acde9481 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -559,3 +559,31 @@ class Foo: bar: Optional[int] = field(default=None) [builtins fixtures/list.pyi] [out] + +[case testMutableProperty] +# flags: --python-version 3.6 +from dataclasses import dataclass +from typing import Optional + +@dataclass(init=False) +class C: + a: Optional[int] + + def __init__(self, a: Optional[int] = None) -> None: + self._a: Optional[int] = None + self.a = a + + @property + def a(self) -> Optional[int]: + return self._a + + @a.setter + def a(self, value: Optional[int]) -> Optional[int]: + self._a = value + return self._a + +c = C() +c.a = 10 +c.a = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "Optional[int]") + +[builtins fixtures/list.pyi] From 10203fe25f1bc8a265416e97df7b07eccbebae9d Mon Sep 17 00:00:00 2001 From: Christian Kanesan Date: Mon, 15 Apr 2019 09:08:18 +0200 Subject: [PATCH 2/2] Skip non variable assignment statements --- mypy/plugins/dataclasses.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index c565b65cd8932..355a7dd2b9bb9 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -3,7 +3,7 @@ from mypy.nodes import ( ARG_OPT, ARG_POS, MDEF, Argument, AssignmentStmt, CallExpr, - Context, Expression, FuncDef, JsonDict, NameExpr, OverloadedFuncDef, + Context, Expression, FuncDef, JsonDict, NameExpr, SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr ) from mypy.plugin import ClassDefContext @@ -204,9 +204,7 @@ def collect_attributes(self) -> List[DataclassAttribute]: continue node = cls.info.names[lhs.name].node - assert isinstance(node, (Var, OverloadedFuncDef)) - - if isinstance(node, OverloadedFuncDef): + if not isinstance(node, Var): continue # x: ClassVar[int] is ignored by dataclasses.