Source code for utils.api.decorators

"""
Décorateurs spécifiques aux appels API.
"""

import time
from functools import wraps
from typing import Callable
from utils.logging_config import get_logger
from utils.config import Config

# Logger pour ce module
logger = get_logger(__name__)

# Configuration centralisée
config = Config()


[docs] def retry_on_429(max_retries: int | None = None, base_delay: float = 7.0): """ Décorateur pour gérer automatiquement les erreurs 429 (Too Many Requests) avec retry et backoff exponentiel. Args: max_retries: Nombre maximum de tentatives (défaut: utilise DEMO_API_MAX_RETRIES de la config) base_delay: Délai de base en secondes (défaut: 7.0) """ def decorator(func: Callable) -> Callable: @wraps(func) def wrapper(*args, **kwargs): last_exception = None # Utiliser la configuration si max_retries n'est pas spécifié actual_max_retries = ( max_retries if max_retries is not None else config.DEMO_API_MAX_RETRIES ) for attempt in range(actual_max_retries + 1): try: return func(*args, **kwargs) except Exception as e: last_exception = e error_str = str(e).lower() # Si c'est une erreur 429 (Too Many Requests), on retry avec backoff if "429" in error_str and "too many requests" in error_str: if attempt < actual_max_retries: # Backoff exponentiel avec délai maximum de 30s delay = min(base_delay * (2**attempt), 30.0) logger.warning( f"Limite API atteinte pour {func.__name__}, " f"attente {delay:.1f}s avant retry {attempt + 1}/{actual_max_retries}" ) time.sleep(delay) continue else: # Pour les autres erreurs, on ne retry pas raise e # Si on arrive ici, toutes les tentatives ont échoué raise last_exception return wrapper return decorator