Faux immutability / the allow_mutation flag seems to have no effect on dataclasses; I suppose this should either be fixed or made explicit in the documentation.
Example:
from pydantic import BaseModel
from pydantic.dataclasses import dataclass
# BaseModel version, which works OK
class Foo(BaseModel):
x: int
class Config:
allow_mutation = False
foo = Foo(x=1)
try:
foo.x = 2
print('Mutated Foo instance OK')
except TypeError as e:
print('Failed to mutate Foo instance')
print(e)
# dataclass version, which doesn't seem to work
class NoMutationConfig:
allow_mutation = False
@dataclass(config=NoMutationConfig)
class Bar:
x: int
bar = Bar(x=1)
try:
bar.x = 2
print('Mutated Bar instance OK')
except TypeError as e:
print('Failed to mutate Bar instance')
print(e)
Output; BaseModel version raises exception as expected, but dataclass version doesn't:
$ python ./pydcheck.py
Failed to mutate Foo instance
"Foo" is immutable and does not support item assignment
Mutated Bar instance OK
you should use frozen on the dataclass.
But it should be possible for us to populate frozen from allow_mutation on the config. PR welcome.
To be concrete about using frozen, running this:
from pydantic.dataclasses import dataclass
@dataclass(frozen=True)
class Bar:
x: int
bar = Bar(x=1)
bar.x = 2
produces this:
$ python bug639.py
Traceback (most recent call last):
File "bug639.py", line 8, in <module>
bar.x = 2
File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'x'
One thing I really like about using @dataclass(frozen=True) is that mypy, by default, treats any attempt to set an instance variable as an error:
$ mypy bug639.py
bug639.py:8: error: Property "x" defined in "Bar" is read-only
Found 1 error in 1 file (checked 1 source file)
For me, this is a solid advantage for using Pydantic's baseclass instead of BaseModel. mypy does not recognize BaseModel/Config allow_mutation = False, meaning any bad code is only detected at runtime.
@bruceadams Once pydantic v1.0 is out I'll finalize work on the mypy plugin #722; it currently does detect allow_mutation = False and will cause mypy errors in the same way that frozen=True does on a dataclass.
(Note: the plugin file is usable now, at least with v0.32.x; I'm using it in all of my fastapi projects.)
Very nice! I look forward to that @dmontagu! Thanks!
Most helpful comment
@bruceadams Once pydantic v1.0 is out I'll finalize work on the mypy plugin #722; it currently does detect
allow_mutation = Falseand will cause mypy errors in the same way thatfrozen=Truedoes on a dataclass.(Note: the plugin file is usable now, at least with v0.32.x; I'm using it in all of my fastapi projects.)