pydantic et son rôle dans la validation et la sérialisation de données typées en PythonFastAPI utilise nativement pydantic pour valider automatiquement les paramètres et le corps d'une requêtepydantic et l'utiliser comme dépendance d'un endpoint pour valider des paramètres de requête (query params)Pour ce TP, utilisez la branche suivante :
git checkout 10_start_pydantic
L'API d'exposition du modèle (exposition/model_as_a_service/inference.py) contient un endpoint /predict qui
accepte aujourd'hui Ws1_avg sans aucun contrôle : n'importe quelle valeur (texte, nombre négatif, chaîne vide...)
est acceptée et transmise telle quelle au modèle. Cela peut provoquer des erreurs silencieuses ou des prédictions
aberrantes en aval, sans jamais donner de message d'erreur clair à l'appelant de l'API.
Nous allons voir comment pydantic permet de garantir, dès la frontière de l'API, que les données reçues respectent un contrat métier (ici : Ws1_avg doit être un entier positif).
pydantic est une bibliothèque Python de validation de données basée sur les annotations de type (type hints). Elle permet de définir des modèles (classes héritant de BaseModel) qui décrivent la forme attendue d'une donnée, puis de valider et convertir automatiquement des données brutes (dictionnaires, JSON, query params...) vers ces modèles.
Concrètement, pydantic permet de :
int, str, float, list, modèles imbriqués, etc.)Field (valeur minimale/maximale, longueur, regex...)ValidationError) si les données ne respectent pas le contratFastAPI est construit au-dessus de pydantic : chaque fois qu'un paramètre d'un endpoint est annoté avec un
type Python (ou un modèle pydantic), FastAPI utilise pydantic pour :
422 Unprocessable Entity avec un message détaillé si la validation échoue/docs)Exemple générique : valider qu'un paramètre de requête est un entier strictement positif.
from typing import Annotated
from fastapi import Depends, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class ItemQueryParams(BaseModel):
quantity: int = Field(gt=0, description="Quantité commandée, doit être un entier positif")
@app.get("/items")
def get_items(params: Annotated[ItemQueryParams, Depends()]):
return {"quantity": params.quantity}
Avec ce code :
GET /items?quantity=3 renvoie {"quantity": 3}GET /items?quantity=-1 renvoie une erreur 422 avec un message expliquant que la valeur doit être supérieure à 0GET /items?quantity=abc renvoie une erreur 422 car la valeur n'est pas convertible en entierAucune ligne de code de validation manuelle (if quantity <= 0: raise ...) n'est nécessaire : pydantic et FastAPI
s'en chargent à partir de la seule déclaration du modèle.
Ws1_avg dans inference.pyExercice : dans exposition/model_as_a_service/inference.py, l'endpoint /predict accepte actuellementWs1_avg comme un simple str, sans validation :
@app.get("/predict")
def predict_endpoint(Ws1_avg: str): # noqa: N803
received_wind_speed_avg = Ws1_avg
...
À vous de jouer :
PredictionQueryParams avec un champ Ws1_avg de type int, contraint à êtreField(gt=0, ...).predict_endpoint pour recevoir ce modèle en dépendance (Annotated[PredictionQueryParams, Depends()])str brut.params.Ws1_avg.Une fois l'exercice réalisé, l'endpoint doit se comporter ainsi :
GET /predict?Ws1_avg=5 : la requête est acceptée, la prédiction est calculée normalementGET /predict?Ws1_avg=-3 : la requête est rejetée avec une erreur 422 explicite (valeur non positive)GET /predict?Ws1_avg=abc : la requête est rejetée avec une erreur 422 explicite (valeur non entière)Pour tester manuellement une fois l'API lancée :
curl "http://localhost:5000/predict?Ws1_avg=5"
curl "http://localhost:5000/predict?Ws1_avg=-3"
curl "http://localhost:5000/predict?Ws1_avg=abc"
La documentation interactive générée automatiquement par FastAPI (http://localhost:5000/docs) reflète elle aussi
la contrainte : le paramètre Ws1_avg y est décrit comme un entier strictement positif.
pydantic ne sert pas qu'aux query params : il permet aussi de valider le corps (body) d'une requêtePOST/PUT en définissant un BaseModel utilisé directement comme type de paramètre de la route.response_model d'une route FastAPI permet de valider et de documenter la forme de la réponse.env), uneconfig.py actuel du projet.Les instructions du tp suivant sont ici