You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
1.9 KiB
Python
84 lines
1.9 KiB
Python
"""Retry wrappers and exception hierarchy."""
|
|
from __future__ import annotations
|
|
|
|
import functools
|
|
from collections.abc import Callable
|
|
from typing import Any, TypeVar
|
|
|
|
from tenacity import (
|
|
RetryError,
|
|
retry,
|
|
retry_if_exception_type,
|
|
stop_after_attempt,
|
|
wait_exponential,
|
|
)
|
|
|
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
|
|
|
|
class ACEFError(Exception):
|
|
"""Base error for ACE-F."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
source: str = "",
|
|
entity: str = "",
|
|
context: dict[str, Any] | None = None,
|
|
) -> None:
|
|
super().__init__(message)
|
|
self.source = source
|
|
self.entity = entity
|
|
self.context = context or {}
|
|
|
|
|
|
class RetryableError(ACEFError):
|
|
"""Transient error that can be retried (network, timeout, 5xx)."""
|
|
|
|
|
|
class NonRetryableError(ACEFError):
|
|
"""Permanent error that must not be retried (404, business rule)."""
|
|
|
|
|
|
class ValidationError(ACEFError):
|
|
"""Schema or data validation failure — never retried."""
|
|
|
|
|
|
class DependencyError(ACEFError):
|
|
"""Required upstream dependency unavailable."""
|
|
|
|
|
|
def with_retry(
|
|
max_attempts: int = 3,
|
|
min_wait: float = 1.0,
|
|
max_wait: float = 30.0,
|
|
multiplier: float = 2.0,
|
|
) -> Callable[[F], F]:
|
|
"""Decorator: retry on RetryableError with exponential backoff."""
|
|
|
|
def decorator(func: F) -> F:
|
|
@retry(
|
|
retry=retry_if_exception_type(RetryableError),
|
|
stop=stop_after_attempt(max_attempts),
|
|
wait=wait_exponential(multiplier=multiplier, min=min_wait, max=max_wait),
|
|
reraise=True,
|
|
)
|
|
@functools.wraps(func)
|
|
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
return await func(*args, **kwargs)
|
|
|
|
return wrapper # type: ignore[return-value]
|
|
|
|
return decorator
|
|
|
|
|
|
__all__ = [
|
|
"ACEFError",
|
|
"RetryableError",
|
|
"NonRetryableError",
|
|
"ValidationError",
|
|
"DependencyError",
|
|
"with_retry",
|
|
"RetryError",
|
|
]
|