Mypy
Pydantic works well with mypy right out of the box.
However, Pydantic also ships with a mypy plugin that adds a number of important Pydantic-specific features that improve its ability to type-check your code.
For example, consider the following script:
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class Model(BaseModel):
age: int
first_name = 'John'
last_name: Optional[str] = None
signup_ts: Optional[datetime] = None
list_of_ints: list[int]
m = Model(age=42, list_of_ints=[1, '2', b'3'])
print(m.middle_name) # not a model field!
Model() # will raise a validation error for age and list_of_ints
Without any special configuration, mypy does not catch the missing model field annotation
and errors about the list_of_ints argument which Pydantic parses correctly:
15: error: List item 1 has incompatible type "str"; expected "int" [list-item]
15: error: List item 2 has incompatible type "bytes"; expected "int" [list-item]
16: error: "Model" has no attribute "middle_name" [attr-defined]
17: error: Missing named argument "age" for "Model" [call-arg]
17: error: Missing named argument "list_of_ints" for "Model" [call-arg]
But with the plugin enabled, it gives the correct errors:
9: error: Untyped fields disallowed [pydantic-field]
16: error: "Model" has no attribute "middle_name" [attr-defined]
17: error: Missing named argument "age" for "Model" [call-arg]
17: error: Missing named argument "list_of_ints" for "Model" [call-arg]
With the pydantic mypy plugin, you can fearlessly refactor your models knowing mypy will catch any mistakes if your field names or types change.
Note that mypy already supports some features without using the Pydantic plugin, such as synthesizing a __init__
method for Pydantic models and dataclasses. See the mypy plugin capabilities for a list
of additional features.
The Pydantic mypy plugin is tested against the latest mypy version. Older versions might work but won’t be tested.
To enable the plugin, just add pydantic.mypy to the list of plugins in your
mypy config file:
[mypy]
plugins = pydantic.mypy
[tool.mypy]
plugins = ['pydantic.mypy']
See the plugin configuration for more details.
- Any required fields that don’t have dynamically-determined aliases will be included as required keyword arguments.
- If the
validate_by_namemodel configuration value is set toTrue, the generated signature will use the field names rather than aliases. - The
init_forbid_extraandinit_typedplugin configuration values can further fine-tune the synthesized__init__method.
- The
model_constructmethod is an alternative to model validation when input data is known to be valid and should not be parsed (see the documentation). Because this method performs no runtime validation, static checking is important to detect errors.
- If the
frozenconfiguration is set toTrue, you will get an error if you try mutating a model field (see faux immutability)
- Field with both a
defaultand adefault_factorywill result in an error during static checking. - The type of the
defaultanddefault_factoryvalue must be compatible with the one of the field.
- While defining a field without an annotation will result in a runtime error, the plugin will also emit a type checking error.
See the documentation of the warn_required_dynamic_aliases plugin configuration value.
To change the values of the plugin settings, create a section in your mypy config file called [pydantic-mypy],
and add any key-value pairs for settings you want to override.
A configuration file with all plugin strictness flags enabled (and some other mypy strictness flags, too) might look like:
[mypy]
plugins = pydantic.mypy
follow_imports = silent
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
no_implicit_reexport = True
disallow_untyped_defs = True
[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
[tool.mypy]
plugins = ["pydantic.mypy"]
follow_imports = "silent"
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
no_implicit_reexport = true
disallow_untyped_defs = true
[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
Because Pydantic performs data conversion by default, the following is still valid at runtime:
class Model(BaseModel):
a: int
Model(a='1')
For this reason, the plugin will use Any for field annotations when synthesizing the __init__ method,
unless init_typed is set or strict mode is enabled on the model.
By default, Pydantic allows (and ignores) any extra provided argument:
class Model(BaseModel):
a: int = 1
Model(unrelated=2)
For this reason, the plugin will add an extra **kwargs: Any parameter when synthesizing the __init__ method, unless
init_forbid_extra is set or the extra is set to 'forbid'.
Whether to error when using a dynamically-determined alias or alias generator on a model with
validate_by_name set to False. If such aliases are
present, mypy cannot properly type check calls to __init__. In this case, it will default to
treating all arguments as not required.