跳到内容

Mypy

Pydantic 与 mypy 开箱即用,效果良好。

然而,Pydantic 也附带了一个 mypy 插件,它添加了许多重要的 Pydantic 特有功能,从而提高了其对代码进行类型检查的能力。

例如,考虑以下脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from datetime import datetime

from pydantic import BaseModel


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

在没有任何特殊配置的情况下,mypy 不会捕获 模型字段缺少注解 错误,以及关于 list_of_ints 参数的错误,而 Pydantic 可以正确解析该参数

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]

但是启用插件后,它会给出正确的错误

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]

通过 pydantic mypy 插件,您可以放心地重构您的模型,因为 mypy 会在您的字段名称或类型更改时捕获任何错误。

请注意,mypy 已经支持一些功能,即使不使用 Pydantic 插件,例如为 Pydantic 模型和数据类合成 __init__ 方法。请参阅 mypy 插件功能 以获取其他功能的列表。

启用插件

要启用插件,只需将 pydantic.mypy 添加到您的 mypy 配置文件 中的插件列表中

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

注意

如果您正在使用 pydantic.v1 模型,则需要将 pydantic.v1.mypy 添加到您的插件列表中。

有关更多详细信息,请参阅插件配置

支持的 mypy 版本

Pydantic 支持最近 6 个月内发布的 mypy 版本。旧版本可能仍然可以与插件一起使用,但不会经过测试。发布的 mypy 版本列表可以在 此处 找到。请注意,版本支持策略可能会由贡献者自行决定更改。

Mypy 插件功能

为 Pydantic 模型生成 __init__ 签名

  • 任何没有动态确定的别名的必需字段都将作为必需的关键字参数包含在内。
  • 如果 validate_by_name 模型配置值设置为 True,则生成的签名将使用字段名称而不是别名。
  • init_forbid_extrainit_typed 插件配置值可以进一步微调合成的 __init__ 方法。

model_construct 生成类型化签名

  • model_construct 方法是模型验证的替代方法,当输入数据已知有效且不应解析时(请参阅文档)。由于此方法不执行运行时验证,因此静态检查对于检测错误非常重要。

对冻结模型的支持

  • 如果 frozen 配置设置为 True,如果您尝试修改模型字段,您将收到错误(请参阅伪不可变性

遵循 Fielddefaultdefault_factory 的类型

  • 同时具有 defaultdefault_factory 的字段将在静态检查期间导致错误。
  • defaultdefault_factory 值的类型必须与字段的类型兼容。

警告关于使用未类型化的字段

  • 虽然定义没有注解的字段将导致运行时错误,但插件也会发出类型检查错误。

防止使用必需的动态别名

请参阅 warn_required_dynamic_aliases 插件配置值的文档。

配置插件

要更改插件设置的值,请在您的 mypy 配置文件中创建一个名为 [pydantic-mypy] 的部分,并为您要覆盖的设置添加任何键值对。

启用所有插件严格性标志(以及一些其他 mypy 严格性标志)的配置文件可能如下所示

[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

init_typed

由于 Pydantic 默认执行数据转换,因此以下内容在运行时仍然有效

class Model(BaseModel):
    a: int


Model(a='1')

因此,插件将在合成 __init__ 方法时对字段注解使用 Any,除非设置了 init_typed 或在模型上启用了严格模式

init_forbid_extra

默认情况下,Pydantic 允许(并忽略)任何额外的提供的参数

class Model(BaseModel):
    a: int = 1


Model(unrelated=2)

因此,插件将在合成 __init__ 方法时添加额外的 **kwargs: Any 参数,除非设置了 init_forbid_extraextra 设置为 'forbid'

warn_required_dynamic_aliases

是否在使用动态确定的别名或别名生成器的模型上,当 validate_by_name 设置为 False 时报错。如果存在此类别名,mypy 无法正确地对 __init__ 的调用进行类型检查。在这种情况下,它将默认将所有参数视为非必需参数。

与禁用 Any 的兼容性

某些 mypy 配置选项(例如 disallow_any_explicit)将报错,因为合成的 __init__ 方法包含 Any 注解。要规避此问题,您将必须同时启用 init_forbid_extrainit_typed