"""Shared async Redis client (sessions, rate limits, pub/sub, arq).""" from __future__ import annotations from collections.abc import Awaitable from typing import Any, cast import redis.asyncio as aioredis from app.config import get_settings def aw(result: Any) -> Awaitable[Any]: """Normalize a redis-py ``ResponseT`` (``Awaitable | value``) to an awaitable. Some redis-py async commands (hash ops, getdel, ...) are typed as a sync/async union; this keeps call sites awaitable under ``mypy --strict``. """ return cast("Awaitable[Any]", result) _client: aioredis.Redis | None = None def get_redis() -> aioredis.Redis: """Return (creating on first use) the process-wide Redis client.""" global _client if _client is None: _client = aioredis.Redis.from_url( get_settings().redis_url, encoding="utf-8", decode_responses=True, ) return _client async def close_redis() -> None: """Close the Redis client (used on app shutdown).""" global _client if _client is not None: await _client.aclose() _client = None