Mypy plugin
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 to mypy that improve its ability to type-check your code.
For example, consider the following script:
from datetime import datetime from typing import List, Optional from pydantic import BaseModel, NoneStr class Model(BaseModel): age: int first_name = 'John' last_name: NoneStr = 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 catches one of the errors (see here for usage instructions):
13: error: "Model" has no attribute "middle_name"
But with the plugin enabled, it catches both:
13: error: "Model" has no attribute "middle_name" 16: error: Missing named argument "age" for "Model" 16: error: Missing named argument "list_of_ints" for "Model"
With the pydantic mypy plugin, you can fearlessly refactor your models knowing mypy will catch any mistakes if your field names or types change.
There are other benefits too! See below for more details.
Plugin Capabilities🔗
Generate a signature for Model.__init__
🔗
- Any required fields that don't have dynamically-determined aliases will be included as required keyword arguments.
- If
Config.allow_population_by_field_name=True
, the generated signature will use the field names, rather than aliases. - For subclasses of
BaseSettings
, all fields are treated as optional since they may be read from the environment. - If
Config.extra="forbid"
and you don't make use of dynamically-determined aliases, the generated signature will not allow unexpected inputs. - Optional: If the
init_forbid_extra
plugin setting is set toTrue
, unexpected inputs to__init__
will raise errors even ifConfig.extra
is not"forbid"
. - Optional: If the
init_typed
plugin setting is set toTrue
, the generated signature will use the types of the model fields (otherwise they will be annotated asAny
to allow parsing).
Generate a typed signature for Model.construct
🔗
- The
construct
method is a faster alternative to__init__
when input data is known to be valid and does not need to be parsed. But because this method performs no runtime validation, static checking is important to detect errors.
Respect Config.allow_mutation
🔗
- If
Config.allow_mutation
isFalse
, you'll get a mypy error if you try to change the value of a model field; cf. faux immutability.
Respect Config.orm_mode
🔗
- If
Config.orm_mode
isFalse
, you'll get a mypy error if you try to call.from_orm()
; cf. ORM mode
Generate a signature for dataclasses
🔗
- classes decorated with
@pydantic.dataclasess.dataclass
are type checked the same as standard python dataclasses - The
@pydantic.dataclasess.dataclass
decorator accepts aconfig
keyword argument which has the same meaning as theConfig
sub-class.
Optional Capabilites:🔗
Prevent the use of required dynamic aliases🔗
- If the
warn_required_dynamic_aliases
plugin setting is set toTrue
, you'll get a mypy error any time you use a dynamically-determined alias or alias generator on a model withConfig.allow_population_by_field_name=False
. - This is important because if such aliases are present, mypy cannot properly type check calls to
__init__
. In this case, it will default to treating all arguments as optional.
Prevent the use of untyped fields🔗
- If the
warn_untyped_fields
plugin setting is set toTrue
, you'll get a mypy error any time you create a field on a model without annotating its type. - This is important because non-annotated fields may result in validators being applied in a surprising order.
- In addition, mypy may not be able to correctly infer the type of the field, and may miss checks or raise spurious errors.
Enabling the Plugin🔗
To enable the plugin, just add pydantic.mypy
to the list of plugins in your
mypy config file
(this could be mypy.ini
or setup.cfg
).
To get started, all you need to do is create a mypy.ini
file with following contents:
[mypy] plugins = pydantic.mypy
See the mypy usage and plugin configuration docs for more details.
Plugin Settings🔗
The plugin offers a few optional strictness flags if you want even stronger checks:
-
init_forbid_extra
If enabled, disallow extra arguments to the
__init__
call even whenConfig.extra
is not"forbid"
. -
init_typed
If enabled, include the field types as type hints in the generated signature for the
__init__
method. This means that you'll get mypy errors if you pass an argument that is not already the right type to__init__
, even if parsing could safely convert the type. -
warn_required_dynamic_aliases
If enabled, raise a mypy error whenever a model is created for which calls to its
__init__
orconstruct
methods require the use of aliases that cannot be statically determined. This is the case, for example, ifallow_population_by_field_name=False
and the model uses an alias generator. -
warn_untyped_fields
If enabled, raise a mypy error whenever a field is declared on a model without explicitly specifying its type.
Configuring the Plugin🔗
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 mypy.ini
file with all plugin strictness flags enabled (and some other mypy strictness flags, too) might look like:
[mypy] plugins = pydantic.mypy follow_imports = silent strict_optional = True warn_redundant_casts = True warn_unused_ignores = True disallow_any_generics = True check_untyped_defs = True no_implicit_reexport = True # for strict mypy: (this is the tricky one :-)) disallow_untyped_defs = True [pydantic-mypy] init_forbid_extra = True init_typed = True warn_required_dynamic_aliases = True warn_untyped_fields = True