Mypy
Pydantic хорошо работает с mypy прямо из коробки.
Однако Pydantic также поставляется с плагином mypy, который добавляет в mypy ряд важных функций, специфичных для pydantic, которые улучшают его способность проверять тип вашего кода.
Например, рассмотрим следующий сценарий:
from datetime import datetime
from typing import List, 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
Без какой-либо специальной настройки mypy не улавливает отсутствующую аннотацию поля модели и предупреждает об аргументе list_of_ints
, который Pydantic правильно анализирует:
test.py:15: error: List item 1 has incompatible type "str"; expected "int" [list-item]
test.py:15: error: List item 2 has incompatible type "bytes"; expected "int" [list-item]
test.py:16: error: "Model" has no attribute "middle_name" [attr-defined]
test.py:17: error: Missing named argument "age" for "Model" [call-arg]
test.py: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 без плагина¶
Вы можете запустить свой код через mypy с помощью:
mypy \
--ignore-missing-imports \
--follow-imports=skip \
--strict-optional \
pydantic_mypy_test.py
Строгий Факультативный¶
Чтобы ваш код прошел с --strict-optional
, вам необходимо использовать Optional[]
или псевдоним Optional[]
для всех полей с None
по умолчанию. (Это стандартно для mypy.)
Другие интерфейсы Pydantic¶
Классы данных Pydantic и декоратор validate_call
также должны хорошо работать с mypy.
Возможности плагина Mypy¶
Создайте подпись для Model.__init__
¶
- Любые обязательные поля, не имеющие динамически определенных псевдонимов, будут включены в качестве обязательных аргументов ключевого слова.
- Если
Config.populate_by_name=True
, сгенерированная подпись будет использовать имена полей, а не псевдонимы. - Если
Config.extra='forbid'
и вы не используете динамически определяемые псевдонимы, сгенерированная подпись не допустит неожиданных входных данных. - Необязательно: если для параметра плагина
init_forbid_extra
установлено значениеTrue
, неожиданные входные данные в__init__
вызовут ошибки, даже еслиConfig.extra
не имеет значения'forbid'
. - Необязательно: если для параметра плагина
init_typed
установлено значениеTrue
, сгенерированная подпись будет использовать типы полей модели (в противном случае они будут помечены какAny
чтобы разрешить анализ).
Создайте типизированную подпись для Model.model_construct
¶
- Метод
model_construct
является альтернативой__init__
, когда известно, что входные данные действительны и не должны анализироваться. Поскольку этот метод не выполняет проверку во время выполнения, для обнаружения ошибок важна статическая проверка.
Соблюдайте Config.frozen
¶
- Если
Config.frozen
имеетTrue
, вы получите ошибку mypy, если попытаетесь изменить значение поля модели; ср. ложная неизменяемость .
Создать подпись для dataclasses
¶
- классы, украшенные
@pydantic.dataclasses.dataclass
проверяются ли типы так же, как и стандартные классы данных Python @pydantic.dataclasses.dataclass
Декоратор принимает аргумент ключевого словаconfig
, который имеет то же значение, что и подклассConfig
.
Уважайте тип Field
default
и default_factory
¶
- Поле со
default
иdefault_factory
приведет к ошибке во время статической проверки. - Тип значения
default
иdefault_factory
должен быть совместим с типом поля.
Предупреждать об использовании нетипизированных полей¶
- Вы получите сообщение об ошибке mypy каждый раз, когда назначаете общедоступный атрибут модели без аннотации его типа.
- Если ваша цель — установить ClassVar, вам следует явно аннотировать поле, используя typing.ClassVar.
Дополнительные возможности:¶
Запретить использование необходимых динамических псевдонимов¶
- Если для параметра плагина
warn_required_dynamic_aliases
установлено значениеTrue
, вы будете получать ошибку mypy каждый раз, когда используете динамически определяемый псевдоним или генератор псевдонимов в модели сConfig.populate_by_name=False
. - Это важно, поскольку если такие псевдонимы присутствуют, mypy не сможет правильно набирать проверочные вызовы
__init__
. В этом случае по умолчанию все аргументы будут рассматриваться как необязательные.
Включение плагина¶
Чтобы включить плагин, просто добавьте pydantic.mypy
в список плагинов в файле конфигурации mypy (это может быть mypy.ini
, pyproject.toml
или setup.cfg
).
Для начала все, что вам нужно сделать, это создать файл mypy.ini
со следующим содержимым:
[mypy]
plugins = pydantic.mypy
!!! Примечание. Если вы используете модели pydantic.v1
, вам необходимо добавить pydantic.v1.mypy
в список плагинов.
Плагин совместим с версиями mypy >=0.930
.
Дополнительную информацию см. в документации по настройке плагина .
Настройка плагина¶
Чтобы изменить значения настроек плагина, создайте в файле конфигурации mypy раздел под названием [pydantic-mypy]
и добавьте любые пары ключ-значение для настроек, которые вы хотите переопределить.
Файл mypy.ini
со всеми включенными флагами строгости плагина (а также некоторыми другими флагами строгости mypy) может выглядеть так:
[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
Начиная с mypy>=0.900
, конфигурация mypy также может быть включена в файл pyproject.toml
а не в mypy.ini
. Та же конфигурация, что и выше, будет:
[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
Примечание по --disallow-any-explicit
¶
Если вы используете параметр конфигурации mypy --disallow-any-explicit
(или другие параметры, запрещающие Any
), вы можете столкнуться с ошибками no-any-explicit
при расширении BaseModel
. Это связано с тем, что по умолчанию плагин mypy
от Pydantic добавляет метод __init__
с сигнатурой типа def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):
!!! Примечание «Зачем лишняя подпись?» Плагин Pydantic mypy
добавляет метод __init__
с сигнатурой типа def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):
во избежание ошибок типов при инициализации моделей с типами, не соответствующими аннотациям полей. Например, Model(date='2024-01-01')
вызовет ошибку типа без этой подписи Any
, но Pydantic имеет возможность анализировать строку '2024-01-01'
в тип datetime.date
.
Чтобы решить эту проблему, вам необходимо включить настройки строгого режима для плагина Pydantic mypy. В частности, добавьте эти параметры в раздел [pydantic-mypy]
:
[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
Если init_forbid_extra = True
**kwargs
удаляются из сгенерированной подписи __init__
. При init_typed = True
Any
типы полей заменяются фактическими подсказками типов.
Эта конфигурация позволяет вам использовать --disallow-any-explicit
без возникновения ошибок в ваших моделях Pydantic. Однако имейте в виду, что эта более строгая проверка может пометить некоторые допустимые варианты использования Pydantic (например, передачу строки для поля даты и времени) как ошибки типа.
本文总阅读量次