Pydantic: Faux immutability / `allow_mutation` has no effect on dataclass

Created on 5 Jul 2019  路  4Comments  路  Source: samuelcolvin/pydantic

Bug

  • OS: MacOS Mojave 10.14.5
  • Python version: 3.7.3
  • Pydantic version: 0.29

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
feature request help wanted

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 = 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.)

All 4 comments

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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bartekbrak picture bartekbrak  路  3Comments

iwoloschin picture iwoloschin  路  3Comments

dconathan picture dconathan  路  3Comments

vvoody picture vvoody  路  3Comments

samuelcolvin picture samuelcolvin  路  3Comments