43 lines
1.1 KiB
Python
43 lines
1.1 KiB
Python
"""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
|