cache.py•2.86 kB
"""Simple file-based cache manager for API results."""
import json
import os
import time
from pathlib import Path
from typing import Any, Optional
class CacheManager:
"""Manages file-based caching of API results."""
def __init__(self, cache_dir: str = ".cache"):
"""Initialize cache manager.
Args:
cache_dir: Directory to store cache files
"""
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(parents=True, exist_ok=True)
def _get_cache_path(self, key: str) -> Path:
"""Get the file path for a cache key."""
# Sanitize key to be filesystem-safe
safe_key = "".join(c if c.isalnum() or c in "._-" else "_" for c in key)
return self.cache_dir / f"{safe_key}.json"
def get(self, key: str, max_age: Optional[int] = None) -> Optional[Any]:
"""Get a value from cache.
Args:
key: Cache key
max_age: Maximum age in seconds (None for no expiration)
Returns:
Cached value or None if not found/expired
"""
cache_path = self._get_cache_path(key)
if not cache_path.exists():
return None
try:
with open(cache_path, "r", encoding="utf-8") as f:
cache_data = json.load(f)
# Check expiration
if max_age is not None:
cached_time = cache_data.get("timestamp", 0)
if time.time() - cached_time > max_age:
return None
return cache_data.get("data")
except (json.JSONDecodeError, KeyError, OSError):
return None
def set(self, key: str, value: Any) -> None:
"""Set a value in cache.
Args:
key: Cache key
value: Value to cache (must be JSON serializable)
"""
cache_path = self._get_cache_path(key)
cache_data = {
"timestamp": time.time(),
"data": value,
}
try:
with open(cache_path, "w", encoding="utf-8") as f:
json.dump(cache_data, f, indent=2, ensure_ascii=False)
except (OSError, TypeError) as e:
# Log error but don't fail
print(f"Warning: Failed to write cache for key '{key}': {e}")
def clear(self, key: Optional[str] = None) -> None:
"""Clear cache.
Args:
key: Specific key to clear, or None to clear all
"""
if key is not None:
cache_path = self._get_cache_path(key)
if cache_path.exists():
cache_path.unlink()
else:
# Clear all cache files
for cache_file in self.cache_dir.glob("*.json"):
cache_file.unlink()