Skip to content

Commit a33d235

Browse files
propertize Callable attributes before freezing dataclasses (#12383)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
1 parent b7d74f3 commit a33d235

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

mypy/plugins/dataclasses.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ def transform(self) -> None:
210210
)
211211

212212
if decorator_arguments['frozen']:
213+
self._propertize_callables(attributes, settable=False)
213214
self._freeze(attributes)
214215
else:
215216
self._propertize_callables(attributes)
@@ -466,7 +467,9 @@ def _freeze(self, attributes: List[DataclassAttribute]) -> None:
466467
var._fullname = info.fullname + '.' + var.name
467468
info.names[var.name] = SymbolTableNode(MDEF, var)
468469

469-
def _propertize_callables(self, attributes: List[DataclassAttribute]) -> None:
470+
def _propertize_callables(self,
471+
attributes: List[DataclassAttribute],
472+
settable: bool = True) -> None:
470473
"""Converts all attributes with callable types to @property methods.
471474
472475
This avoids the typechecker getting confused and thinking that
@@ -480,7 +483,7 @@ def _propertize_callables(self, attributes: List[DataclassAttribute]) -> None:
480483
var = attr.to_var()
481484
var.info = info
482485
var.is_property = True
483-
var.is_settable_property = True
486+
var.is_settable_property = settable
484487
var._fullname = info.fullname + '.' + var.name
485488
info.names[var.name] = SymbolTableNode(MDEF, var)
486489

test-data/unit/check-dataclasses.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,22 @@ A(1)
15371537
A(a="foo") # E: Argument "a" to "A" has incompatible type "str"; expected "int"
15381538
[builtins fixtures/dataclasses.pyi]
15391539

1540+
[case testDataclassesCallableFrozen]
1541+
# flags: --python-version 3.7
1542+
from dataclasses import dataclass
1543+
from typing import Any, Callable
1544+
@dataclass(frozen=True)
1545+
class A:
1546+
a: Callable[..., None]
1547+
1548+
def func() -> None:
1549+
pass
1550+
1551+
reveal_type(A.a) # N: Revealed type is "def (*Any, **Any)"
1552+
A(a=func).a()
1553+
A(a=func).a = func # E: Property "a" defined in "A" is read-only
1554+
[builtins fixtures/dataclasses.pyi]
1555+
15401556
[case testDataclassesMultipleInheritanceWithNonDataclass]
15411557
# flags: --python-version 3.10
15421558
from dataclasses import dataclass

0 commit comments

Comments
 (0)