Migrated to ruff for faster, modern code formatting and linting: Configuration changes: - pyproject.toml: Added ruff 0.8.6, removed black/flake8 - Configured ruff with black-compatible formatting (88 chars) - Enabled comprehensive linting rules (pycodestyle, pyflakes, isort, pyupgrade, bugbear, comprehensions, simplify, return) - Updated CLAUDE.md: Changed code quality commands to use ruff Code improvements (490 auto-fixes): - Modernized type hints: List[T] → list[T], Dict[K,V] → dict[K,V], Optional[T] → T | None - Sorted all imports (isort integration) - Removed unused imports - Fixed whitespace issues - Reformatted 38 files for consistency Bug fixes: - app/core/play_resolver.py: Fixed type hint bug (any → Any) - tests/unit/core/test_runner_advancement.py: Removed obsolete random mock Testing: - All 739 unit tests passing (100%) - No regressions introduced 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
2.8 KiB
Python
114 lines
2.8 KiB
Python
"""
|
|
Redis connection client for caching.
|
|
|
|
Provides async Redis client with connection pooling for
|
|
position ratings and other cached data.
|
|
|
|
Author: Claude
|
|
Date: 2025-11-03
|
|
Phase: 3E-Final
|
|
"""
|
|
|
|
import logging
|
|
|
|
import redis.asyncio as redis
|
|
from redis.asyncio import Redis
|
|
from redis.exceptions import RedisError
|
|
|
|
logger = logging.getLogger(f"{__name__}.RedisClient")
|
|
|
|
|
|
class RedisClient:
|
|
"""
|
|
Async Redis client with connection pooling.
|
|
|
|
Singleton pattern - use redis_client instance.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._redis: Redis | None = None
|
|
self._url: str | None = None
|
|
|
|
async def connect(self, redis_url: str = "redis://localhost:6379/0") -> None:
|
|
"""
|
|
Connect to Redis server.
|
|
|
|
Args:
|
|
redis_url: Redis connection URL
|
|
Format: redis://host:port/db
|
|
Example: redis://localhost:6379/0
|
|
"""
|
|
if self._redis is not None:
|
|
logger.warning("Redis client already connected")
|
|
return
|
|
|
|
try:
|
|
self._url = redis_url
|
|
self._redis = await redis.from_url(
|
|
redis_url,
|
|
encoding="utf-8",
|
|
decode_responses=True,
|
|
max_connections=10, # Connection pool size
|
|
)
|
|
|
|
# Test connection
|
|
await self._redis.ping()
|
|
logger.info(f"Redis connected successfully: {redis_url}")
|
|
|
|
except RedisError as e:
|
|
logger.error(f"Failed to connect to Redis at {redis_url}: {e}")
|
|
self._redis = None
|
|
raise
|
|
|
|
async def disconnect(self) -> None:
|
|
"""Disconnect from Redis server."""
|
|
if self._redis is not None:
|
|
await self._redis.close()
|
|
await self._redis.connection_pool.disconnect()
|
|
self._redis = None
|
|
logger.info("Redis disconnected")
|
|
|
|
@property
|
|
def client(self) -> Redis:
|
|
"""
|
|
Get Redis client instance.
|
|
|
|
Returns:
|
|
Redis client
|
|
|
|
Raises:
|
|
RuntimeError: If not connected
|
|
"""
|
|
if self._redis is None:
|
|
raise RuntimeError(
|
|
"Redis client not connected. "
|
|
"Call await redis_client.connect() first."
|
|
)
|
|
return self._redis
|
|
|
|
@property
|
|
def is_connected(self) -> bool:
|
|
"""Check if Redis is connected."""
|
|
return self._redis is not None
|
|
|
|
async def ping(self) -> bool:
|
|
"""
|
|
Test Redis connection.
|
|
|
|
Returns:
|
|
True if Redis is responding, False otherwise
|
|
"""
|
|
if not self.is_connected:
|
|
return False
|
|
|
|
try:
|
|
await self._redis.ping()
|
|
return True
|
|
except RedisError as e:
|
|
logger.error(f"Redis ping failed: {e}")
|
|
return False
|
|
|
|
|
|
# Singleton instance
|
|
redis_client = RedisClient()
|