import asyncio import functools from typing import Callable, Any, TypeVar, Coroutine from wechat_auto.utils.logger import logger from wechat_auto.config import settings T = TypeVar('T') def async_retry( max_retries: int = None, base_delay: float = None, exponential: bool = True, exceptions: tuple = (Exception,) ): max_retries = max_retries or settings.max_retries base_delay = base_delay or settings.retry_base_delay def decorator(func: Callable[..., Coroutine[Any, Any, T]]): @functools.wraps(func) async def wrapper(*args, **kwargs) -> T: last_exception = None for attempt in range(max_retries): try: return await func(*args, **kwargs) except exceptions as e: last_exception = e if attempt < max_retries - 1: if exponential: delay = base_delay * (2 ** attempt) else: delay = base_delay logger.warning( f"{func.__name__} 失败,{attempt + 1}/{max_retries}," f"{delay:.1f}秒后重试: {e}" ) await asyncio.sleep(delay) else: logger.error( f"{func.__name__} 失败,已达到最大重试次数 {max_retries}: {e}" ) raise last_exception return wrapper return decorator def sync_retry( max_retries: int = None, base_delay: float = None, exponential: bool = True, exceptions: tuple = (Exception,) ): max_retries = max_retries or settings.max_retries base_delay = base_delay or settings.retry_base_delay def decorator(func: Callable[..., T]): @functools.wraps(func) def wrapper(*args, **kwargs) -> T: last_exception = None for attempt in range(max_retries): try: return func(*args, **kwargs) except exceptions as e: last_exception = e if attempt < max_retries - 1: if exponential: delay = base_delay * (2 ** attempt) else: delay = base_delay logger.warning( f"{func.__name__} 失败,{attempt + 1}/{max_retries}," f"{delay:.1f}秒后重试: {e}" ) import time time.sleep(delay) else: logger.error( f"{func.__name__} 失败,已达到最大重试次数 {max_retries}: {e}" ) raise last_exception return wrapper return decorator