import asyncio import json import hashlib from typing import Any, Callable, Dict, Optional from functools import wraps import logging logger = logging.getLogger(__name__) # Simple in-memory cache _cache: Dict[str, Any] = {} _cache_lock = asyncio.Lock() def build_cache_key(namespace: str, *args, **kwargs) -> str: """Build a cache key from namespace and parameters""" key_data = f"{namespace}:{args}:{sorted(kwargs.items())}" return hashlib.md5(key_data.encode()).hexdigest() def cached(expire: int = 300, key_builder: Optional[Callable] = None): """ Cache decorator for FastAPI endpoints Args: expire: Cache expiration time in seconds key_builder: Function to build cache key """ def decorator(func: Callable) -> Callable: @wraps(func) async def wrapper(*args, **kwargs): # Build cache key if key_builder: cache_key = key_builder(func, **kwargs) else: cache_key = build_cache_key(func.__name__, *args, **kwargs) # Check cache async with _cache_lock: if cache_key in _cache: cached_data, timestamp = _cache[cache_key] import time if time.time() - timestamp < expire: logger.debug(f"Cache hit for key: {cache_key}") return cached_data else: # Expired, remove from cache del _cache[cache_key] # Cache miss, execute function logger.debug(f"Cache miss for key: {cache_key}") result = await func(*args, **kwargs) # Store in cache async with _cache_lock: import time _cache[cache_key] = (result, time.time()) return result return wrapper return decorator def setup_cache(): """Setup cache configuration""" logger.info("FastAPI cache initialized with in-memory backend")