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 (например, передачу строки для поля даты и времени) как ошибки типа.
本文总阅读量次