Mypy
Pydantic fonctionne bien avec mypy dès la sortie de la boîte.
Cependant, Pydantic est également livré avec un plugin mypy qui ajoute à mypy un certain nombre de fonctionnalités importantes spécifiques à pydantic qui améliorent sa capacité à vérifier la saisie de votre code.
Par exemple, considérons le script suivant:
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
Sans aucune configuration spéciale, mypy ne détecte pas l' annotation de champ de modèle manquante et met en garde contre l'argument list_of_ints
que Pydantic analyse correctement:
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]
Mais avec le plugin activé , cela donne la bonne erreur:
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]
Avec le plugin pydantic mypy, vous pouvez sans crainte refactoriser vos modèles en sachant que mypy détectera toute erreur si les noms ou types de vos champs changent.
Il y a aussi d’autres avantages ! Voir ci-dessous pour plus de détails.
Utiliser mypy sans le plugin¶
Vous pouvez exécuter votre code via mypy avec:
mypy \
--ignore-missing-imports \
--follow-imports=skip \
--strict-optional \
pydantic_mypy_test.py
Stricte Facultatif¶
Pour que votre code soit transmis avec --strict-optional
, vous devez utiliserOptional Optional[]
ou un alias de Optional[]
pour tous les champs avec None
par défaut. (C'est standard avec mypy.)
Autres interfaces Pydantiques¶
Les classes de données Pydantic et le décorateur validate_call
devraient également bien fonctionner avec mypy.
Capacités du plugin Mypy¶
Générer une signature pour Model.__init__
¶
- Tous les champs obligatoires qui n'ont pas d'alias déterminés dynamiquement seront inclus en tant qu'arguments de mot clé obligatoires.
- Si
Config.populate_by_name=True
, la signature générée utilisera les noms de champs plutôt que les alias. - Si
Config.extra='forbid'
et que vous n'utilisez pas d'alias déterminés dynamiquement, la signature générée n'autorisera pas les entrées inattendues. - Facultatif: si le paramètre du plugin
init_forbid_extra
est défini surTrue
, les entrées inattendues dans__init__
généreront des erreurs même siConfig.extra
n'est pas'forbid'
. - Facultatif: Si le paramètre du plugin
init_typed
est défini surTrue
, la signature générée utilisera les types des champs du modèle (sinon, ils seront annotés commeAny
pour permettre l'analyse).
Générer une signature tapée pour Model.model_construct
¶
- La méthode
model_construct
est une alternative à__init__
lorsque les données d'entrée sont connues pour être valides et ne doivent pas être analysées. Étant donné que cette méthode n'effectue aucune validation à l'exécution, la vérification statique est importante pour détecter les erreurs.
Respecter Config.frozen
¶
- Si
Config.frozen
vautTrue
, vous obtiendrez une erreur mypy si vous essayez de modifier la valeur d'un champ de modèle; cf. fausse immuabilité .
Générer une signature pour dataclasses
¶
- classes décorées avec
@pydantic.dataclasses.dataclass
sont vérifiés de la même manière que les classes de données Python standard - Le
@pydantic.dataclasses.dataclass
decorator accepte un argument de mot-cléconfig
qui a la même signification que la sous-classeConfig
.
Respecter le type du Field
default
et default_factory
¶
- Un champ avec à la fois un
default
et undefault_factory
entraînera une erreur lors de la vérification statique. - Le type de la valeur
default
etdefault_factory
doit être compatible avec celui du champ.
Avertir de l'utilisation de champs non typés¶
- Vous obtiendrez une erreur mypy chaque fois que vous attribuerez un attribut public à un modèle sans annoter son type.
- Si votre objectif est de définir un ClassVar, vous devez annoter explicitement le champ en utilisant typing.ClassVar
Capacités optionnelles:¶
Empêcher l'utilisation des alias dynamiques requis¶
- Si le paramètre du plugin
warn_required_dynamic_aliases
est défini surTrue
, vous obtiendrez une erreur mypy chaque fois que vous utiliserez un alias ou un générateur d'alias déterminé dynamiquement sur un modèle avecConfig.populate_by_name=False
. - Ceci est important car si de tels alias sont présents, mypy ne peut pas taper correctement les appels de vérification à
__init__
. Dans ce cas, tous les arguments seront traités par défaut comme facultatifs.
Activation du plugin¶
Pour activer le plugin, ajoutez simplement pydantic.mypy
à la liste des plugins dans votre fichier de configuration mypy (cela pourrait être mypy.ini
, pyproject.toml
ou setup.cfg
).
Pour commencer, tout ce que vous avez à faire est de créer un fichier mypy.ini
avec le contenu suivant:
[mypy]
plugins = pydantic.mypy
!!! note Si vous utilisez des modèles pydantic.v1
, vous devrez ajouter pydantic.v1.mypy
à votre liste de plugins.
Le plugin est compatible avec les versions mypy >=0.930
.
Consultez la documentation de configuration du plugin pour plus de détails.
Configuration du plugin¶
Pour modifier les valeurs des paramètres du plugin, créez une section dans votre fichier de configuration mypy appelée [pydantic-mypy]
et ajoutez toutes les paires clé-valeur pour les paramètres que vous souhaitez remplacer.
Un fichier mypy.ini
avec tous les indicateurs de rigueur du plugin activés (et quelques autres indicateurs de rigueur mypy également) pourrait ressembler à:
[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
À partir de mypy>=0.900
, la configuration mypy peut également être incluse dans le fichier pyproject.toml
plutôt que dans mypy.ini
. La même configuration que ci-dessus serait:
[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
Remarque sur --disallow-any-explicit
¶
Si vous utilisez le paramètre de configuration --disallow-any-explicit
mypy (ou d'autres paramètres qui interdisent Any
), vous risquez de rencontrer des erreurs no-any-explicit
lors de l'extension BaseModel
. En effet, par défaut, le plugin mypy
de Pydantic ajoute une méthode __init__
avec une signature comme def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):
!!! note "Pourquoi la signature supplémentaire ?" Le plugin Pydantic mypy
ajoute une méthode __init__
avec une signature comme def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):
afin d'éviter les erreurs de type lors de l'initialisation de modèles avec des types qui ne correspondent pas aux annotations de champ. Par exemple, Model(date='2024-01-01')
générerait une erreur de type sans cette signature Any
, mais Pydantic a la capacité d'analyser la chaîne '2024-01-01'
dans un type datetime.date
.
Pour résoudre ce problème, vous devez activer les paramètres du mode strict pour le plugin Pydantic mypy. Plus précisément, ajoutez ces options à votre section [pydantic-mypy]
:
[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
Avec init_forbid_extra = True
, les **kwargs
sont supprimés de la signature __init__
générée. Avec init_typed = True
, les types Any
des champs sont remplacés par leurs indications de type réelles.
Cette configuration vous permet d'utiliser --disallow-any-explicit
sans obtenir d'erreurs sur vos modèles Pydantic. Cependant, sachez que cette vérification plus stricte peut signaler certains cas d'utilisation valides de Pydantic (comme le passage d'une chaîne pour un champ datetime) comme des erreurs de type.
本文总阅读量次