Pydantic: allow mutation, forbid to change types

Viewed 1494

Is there any way to forbid changing types of mutated Pydantic models? For example,

from pydantic import BaseModel

class AppConfig(BaseModel):

    class Config:
        allow_mutation = True
    
    a: int = 33
    b: float = 22.0

I want to be able to change the fields, like:

config = AppConfig()
config.a = 44

But I want to forbid changing the fields' types, like:

config.a = '44'
<Some error message here>
1 Answers

One key thing about Pydantic that is important to understand is that it will try to coerce to the type you've annotated. E.g. it will try calling int on whatever input you give for a. If you don't want that behavior, use strict types:

In [1]: from pydantic import BaseModel, StrictInt, StrictFloat
   ...:
   ...: class AppConfig(BaseModel):
   ...:
   ...:     class Config:
   ...:         allow_mutation = True
   ...:
   ...:     a: StrictInt = 33
   ...:     b: StrictFloat = 22.0
   ...:

In [2]: AppConfig(a=42, b=0.0)
Out[2]: AppConfig(a=42, b=0.0)

In [3]: AppConfig(a='42', b=0.0)
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-3-702f4ffeef8d> in <module>
----> 1 AppConfig(a='42', b=0.0)

~/opt/miniconda3/envs/wrangle/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for AppConfig
a
  value is not a valid integer (type=type_error.integer)

Additionally, by default pydantic won't perform validation on assignment to attributes, to change that behavior you need to set validate_assignment in your Config, so you need:

In [1]: from pydantic import BaseModel, StrictInt, StrictFloat
   ...:
   ...: class AppConfig(BaseModel):
   ...:
   ...:     class Config:
   ...:         allow_mutation = True
   ...:         validate_assignment = True
   ...:
   ...:     a: StrictInt = 33
   ...:     b: StrictFloat = 22.0
   ...:

In [2]: config = AppConfig()

In [3]: config
Out[3]: AppConfig(a=33, b=22.0)

In [4]: config.a = '42'
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-4-39b2a238b39e> in <module>
----> 1 config.a = '42'

~/opt/miniconda3/envs/wrangle/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.__setattr__()

ValidationError: 1 validation error for AppConfig
a
  value is not a valid integer (type=type_error.integer)
Related