
Fai code review di questa implementazione cercando bug reali...
Prompt
Fai code review di questa implementazione cercando bug reali ed edge case, senza riscriverla se è corretta. import asyncio from typing import Callable, Awaitable, TypeVar, Tuple, Type, Optional T = TypeVar("T") async def retry_async( fn: Callable[[], Awaitable[T]], *, attempts: int = 3, base_delay: float = 0.1, retry_on: Tuple[Type[Exception], ...] = (Exception,), sleep: Callable[[float], Awaitable[object]] = asyncio.sleep, ) -> T: """Retry an async callable on selected exceptions with exponential back‑off. Args: fn: Async callable that takes no arguments. attempts: Maximum number of **total** attempts (including the first one). base_delay: Initial delay between retries (seconds). retry_on: Tuple of exception types that should be retried. sleep: Async function used to wait (default ``asyncio.sleep``); injected for easier testing. Returns: The result returned by ``fn``. Raises: ValueError: If ``attempts`` is less than 1. Exception: The last exception that failed after all attempts, provided it matches ``retry_on``. """ if attempts < 1: raise ValueError("attempts must be >= 1") last_exc: Optional[Exception] = None for attempt in range(1, attempts + 1): try: return await fn() except Exception as exc: # pragma: no‑cover – we want to be explicit if not isinstance(exc, retry_on): raise # non‑retryable → propagate immediately last_exc = exc # No sleep after the final attempt if attempt == attempts: break # exponential back‑off: base_delay * 2^(attempt‑1) delay = base_delay * (2 ** (attempt - 1)) await sleep(delay) # All attempts exhausted – re‑raise the last retryable exception raise last_exc