Mode strict
??? API "Documentation API" pydantic.types.Strict
Par défaut, Pydantic tentera de contraindre les valeurs au type souhaité lorsque cela est possible. Par exemple, vous pouvez transmettre la chaîne "123"
comme entrée dans un champ int
et elle sera convertie en 123
. Ce comportement de coercition est utile dans de nombreux scénarios: pensez aux UUID, aux paramètres d'URL, aux en-têtes HTTP, aux variables d'environnement, aux entrées utilisateur, etc.
Cependant, il existe également des situations dans lesquelles cela n'est pas souhaitable et vous souhaitez que Pydantic génère une erreur au lieu de forcer les données.
Pour mieux prendre en charge ce cas d'utilisation, Pydantic propose un « mode strict » qui peut être activé par modèle, par champ ou même par appel de validation. Lorsque le mode strict est activé, Pydantic sera beaucoup moins indulgent lors de la contrainte de données et générera plutôt une erreur si les données ne sont pas du type correct.
Voici un bref exemple montrant la différence entre le comportement de validation en mode strict et le mode par défaut/"lax":
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
x: int
print(MyModel.model_validate({'x': '123'})) # lax mode
#> x=123
try:
MyModel.model_validate({'x': '123'}, strict=True) # strict mode
except ValidationError as exc:
print(exc)
"""
1 validation error for MyModel
x
Input should be a valid integer [type=int_type, input_value='123', input_type=str]
"""
Il existe différentes manières d'obtenir une validation en mode strict lors de l'utilisation de Pydantic, qui seront abordées plus en détail ci-dessous:
- Passer
strict=True
aux méthodes de validation , telles queBaseModel.model_validate
,TypeAdapter.validate_python
et similaire pour JSON - Utilisation
Field(strict=True)
avec les champs d'unBaseModel
,dataclass
ouTypedDict
- Utiliser
pydantic.types.Strict
comme annotation de type sur un champ- Pydantic fournit des alias de types déjà annotés avec
Strict
, tels quepydantic.types.StrictInt
- Pydantic fournit des alias de types déjà annotés avec
- Utilisation
ConfigDict(strict=True)
Tapez les coercitions en mode strict¶
Pour la plupart des types, lors de la validation des données de Python en mode strict, seules les instances des types exacts sont acceptées. Par exemple, lors de la validation d'un champ int
, seules les instances de int
sont acceptées; le passage d'instances de float
ou str
entraînera la levée d'une ValidationError
.
Notez que nous sommes plus lâches lors de la validation des données de JSON en mode strict. Par exemple, lors de la validation d'un champ UUID
, les instances de str
seront acceptées lors de la validation depuis JSON, mais pas depuis python:
import json
from uuid import UUID
from pydantic import BaseModel, ValidationError
class MyModel(BaseModel):
guid: UUID
data = {'guid': '12345678-1234-1234-1234-123456789012'}
print(MyModel.model_validate(data)) # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')
print(
MyModel.model_validate_json(json.dumps(data), strict=True)
) # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')
try:
MyModel.model_validate(data, strict=True) # Not OK: strict, from python
except ValidationError as exc:
print(exc.errors(include_url=False))
"""
[
{
'type': 'is_instance_of',
'loc': ('guid',),
'msg': 'Input should be an instance of UUID',
'input': '12345678-1234-1234-1234-123456789012',
'ctx': {'class': 'UUID'},
}
]
"""
Pour plus de détails sur les types autorisés comme entrées en mode strict, vous pouvez consulter la table de conversion .
Mode strict dans les appels de méthode¶
Tous les exemples inclus jusqu'à présent obtiennent une validation en mode strict grâce à l'utilisation de strict=True
comme argument mot-clé pour les méthodes de validation. Bien que nous l'ayons montré pour BaseModel.model_validate
, cela fonctionne également avec des types arbitraires grâce à l'utilisation de TypeAdapter
:
from pydantic import TypeAdapter, ValidationError
print(TypeAdapter(bool).validate_python('yes')) # OK: lax
#> True
try:
TypeAdapter(bool).validate_python('yes', strict=True) # Not OK: strict
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
Notez que cela fonctionne également même lors de l'utilisation de types plus "complexes" dans TypeAdapter
:
from dataclasses import dataclass
from pydantic import TypeAdapter, ValidationError
@dataclass
class MyDataclass:
x: int
try:
TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
print(exc)
"""
1 validation error for MyDataclass
Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
"""
Cela fonctionne également avec les méthodes TypeAdapter.validate_json
et BaseModel.model_validate_json
:
import json
from typing import List
from uuid import UUID
from pydantic import BaseModel, TypeAdapter, ValidationError
try:
TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
print(exc)
"""
2 validation errors for list[int]
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
2
Input should be a valid integer [type=int_type, input_value='3', input_type=str]
"""
class Model(BaseModel):
x: int
y: UUID
data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
Model.model_validate(data, strict=True)
except ValidationError as exc:
# Neither x nor y are valid in strict mode from python:
print(exc)
"""
2 validation errors for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
y
Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
"""
json_data = json.dumps(data)
try:
Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
# From JSON, x is still not valid in strict mode, but y is:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Mode strict avec Field
¶
Pour les champs individuels d'un modèle, vous pouvez définir strict=True
sur le field . Cela entraînera l'utilisation d'une validation en mode strict pour ce champ, même lorsque les méthodes de validation sont appelées sans strict=True
.
Seuls les champs pour lesquels strict=True
est défini seront affectés:
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
name: str
age: int
n_pets: int
user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1
class AnotherUser(BaseModel):
name: str
age: int = Field(strict=True)
n_pets: int
try:
anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
print(e)
"""
1 validation error for AnotherUser
age
Input should be a valid integer [type=int_type, input_value='42', input_type=str]
"""
Notez que rendre les champs stricts affectera également la validation effectuée lors de l'instanciation de la classe de modèle:
from pydantic import BaseModel, Field, ValidationError
class Model(BaseModel):
x: int = Field(strict=True)
y: int = Field(strict=False)
try:
Model(x='1', y='2')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Utiliser Field
comme annotation¶
Notez que Field(strict=True)
(ou avec tout autre argument de mot-clé) peut être utilisé comme annotation si nécessaire, par exemple lorsque vous travaillez avec TypedDict
:
from typing_extensions import Annotated, TypedDict
from pydantic import Field, TypeAdapter, ValidationError
class MyDict(TypedDict):
x: Annotated[int, Field(strict=True)]
try:
TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
Mode strict avec Annotated[..., Strict()]
¶
??? API "Documentation API" pydantic.types.Strict
Pydantic fournit également la classe Strict
, qui est destinée à être utilisée comme métadonnées avec la classe typing.Annotated
; cette annotation indique que le champ annoté doit être validé en mode strict :
from typing_extensions import Annotated
from pydantic import BaseModel, Strict, ValidationError
class User(BaseModel):
name: str
age: int
is_active: Annotated[bool, Strict()]
User(name='David', age=33, is_active=True)
try:
User(name='David', age=33, is_active='True')
except ValidationError as exc:
print(exc)
"""
1 validation error for User
is_active
Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
"""
Il s'agit en fait de la méthode utilisée pour implémenter certains des types stricts prêts à l'emploi fournis par Pydantic, tels que StrictInt
.
Mode strict avec ConfigDict
¶
BaseModel
¶
Si vous souhaitez activer le mode strict pour tous les champs sur un type d'entrée complexe, vous pouvez utiliser ConfigDict(strict=True)
dans le model_config
:
from pydantic import BaseModel, ConfigDict, ValidationError
class User(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int
is_active: bool
try:
User(name='David', age='33', is_active='yes')
except ValidationError as exc:
print(exc)
"""
2 validation errors for User
age
Input should be a valid integer [type=int_type, input_value='33', input_type=str]
is_active
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
!!! note Lorsque vous utilisez strict=True
via model_config
d'un modèle, vous pouvez toujours remplacer la rigueur de champs individuels en définissant strict=False
sur des champs individuels:
```py
from pydantic import BaseModel, ConfigDict, Field
class User(BaseModel):
model_config = ConfigDict(strict=True)
name: str
age: int = Field(strict=False)
```
Notez que le mode strict n'est pas appliqué de manière récursive aux champs de modèle imbriqués:
from pydantic import BaseModel, ConfigDict, ValidationError
class Inner(BaseModel):
y: int
class Outer(BaseModel):
model_config = ConfigDict(strict=True)
x: int
inner: Inner
print(Outer(x=1, inner=Inner(y='2')))
#> x=1 inner=Inner(y=2)
try:
Outer(x='1', inner=Inner(y='2'))
except ValidationError as exc:
print(exc)
"""
1 validation error for Outer
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
(C'est également le cas pour les classes de données et TypedDict
.)
Si cela n'est pas souhaitable, vous devez vous assurer que le mode strict est activé pour tous les types impliqués. Par exemple, cela peut être fait pour les classes modèles en utilisant une classe de base partagée avec model_config = ConfigDict(strict=True)
:
from pydantic import BaseModel, ConfigDict, ValidationError
class MyBaseModel(BaseModel):
model_config = ConfigDict(strict=True)
class Inner(MyBaseModel):
y: int
class Outer(MyBaseModel):
x: int
inner: Inner
try:
Outer.model_validate({'x': 1, 'inner': {'y': '2'}})
except ValidationError as exc:
print(exc)
"""
1 validation error for Outer
inner.y
Input should be a valid integer [type=int_type, input_value='2', input_type=str]
"""
Classes de données et TypedDict
¶
Les classes de données Pydantic se comportent de la même manière que les exemples présentés ci-dessus avec BaseModel
, juste qu'au lieu de model_config
vous devez utiliser l'argument de mot-clé config
pour le @pydantic.dataclasses.dataclass
décorateur.
Lorsque cela est possible, vous pouvez obtenir un mode strict imbriqué pour les classes de données Vanilla ou les sous-classes TypedDict
en annotant les champs avec l' annotation pydantic.types.Strict
.
Cependant, si cela n'est pas possible (par exemple, lorsque vous travaillez avec des types tiers), vous pouvez définir la configuration que Pydantic doit utiliser pour le type en définissant l'attribut __pydantic_config__
sur le type:
from typing_extensions import TypedDict
from pydantic import ConfigDict, TypeAdapter, ValidationError
class Inner(TypedDict):
y: int
Inner.__pydantic_config__ = ConfigDict(strict=True)
class Outer(TypedDict):
x: int
inner: Inner
adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}
try:
adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
inner.y
Input should be a valid integer [type=int_type, input_value='2', input_type=str]
"""
TypeAdapter
¶
Vous pouvez également obtenir le mode strict en utilisant l'argument mot-clé config de la classe TypeAdapter
:
from pydantic import ConfigDict, TypeAdapter, ValidationError
adapter = TypeAdapter(bool, config=ConfigDict(strict=True))
try:
adapter.validate_python('yes')
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""
@validate_call
¶
Le mode strict est également utilisable avec le décorateur @validate_call
en passant l'argument mot-clé config
:
from pydantic import ConfigDict, ValidationError, validate_call
@validate_call(config=ConfigDict(strict=True))
def foo(x: int) -> int:
return x
try:
foo('1')
except ValidationError as exc:
print(exc)
"""
1 validation error for foo
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
本文总阅读量次