跳转至

Pydantic Company

我们根据我认为导致 Pydantic 成功的原则创办了一家公司。

公司公告中了解更多信息.

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 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
from datetime import datetime
from typing import 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
from datetime import datetime
from pydantic import BaseModel, NoneStr


class Model(BaseModel):
    age: int
    first_name = 'John'
    last_name: NoneStr = None
    signup_ts: datetime | None = 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

Model.__init__ 生成签名 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 to True, unexpected inputs to __init__ will raise errors even if Config.extra is not "forbid".
  • Optional: If the init_typed plugin setting is set to True, the generated signature will use the types of the model fields (otherwise they will be annotated as Any to allow parsing).

Model.construct 生成类型签名 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 is False, 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 is False, you'll get a mypy error if you try to call .from_orm(); cf. ORM mode

dataclasses 生成签名 Generate a signature for dataclasses

注意 Fielddefaultdefault_factory 的类型 Respect the type of the Field's default and default_factory

  • Field with both a default and a default_factory will result in an error during static checking.
  • The type of the default and default_factory value must be compatible with the one of the field.

可选功能 Optional Capabilities:

防止使用所需的动态别名 Prevent the use of required dynamic aliases

  • If the warn_required_dynamic_aliases plugin setting is set to True, you'll get a mypy error any time you use a dynamically-determined alias or alias generator on a model with Config.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

启用插件 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

The plugin is compatible with mypy versions >=0.910.

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 when Config.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__ or construct methods require the use of aliases that cannot be statically determined. This is the case, for example, if allow_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
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

As of mypy>=0.900, mypy config may also be included in the pyproject.toml file rather than mypy.ini. The same configuration as above would be:

[tool.mypy]
plugins = [
  "pydantic.mypy"
]

follow_imports = "silent"
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

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true