databao/caches/disk_cache.py (38 lines of code) (raw):
import json
import pickle
from dataclasses import dataclass
from pathlib import Path
from typing import Any
import diskcache # type: ignore[import-untyped]
from databao.core import Cache
@dataclass(kw_only=True)
class DiskCacheConfig:
db_dir: str | Path = Path("cache/diskcache/")
class DiskCache(Cache):
"""A simple SQLite-backed cache."""
def __init__(self, config: DiskCacheConfig | None = None, cache: diskcache.Cache | None = None, prefix: str = ""):
self.config = config or DiskCacheConfig()
self._cache: diskcache.Cache = cache or diskcache.Cache(str(self.config.db_dir))
self._prefix = prefix
def put(self, key: str, state: dict[str, Any]) -> None:
k = f"{self._prefix}{key}"
self._cache.set(k, value=pickle.dumps(state), tag=self._prefix)
def get(self, key: str, default: dict[str, Any] | None = None) -> dict[str, Any]:
k = f"{self._prefix}{key}"
res_bytes = self._cache.get(k, default=None)
if res_bytes is None:
_default: dict[str, Any] = {} if default is None else default
return _default
result: dict[str, Any] = pickle.loads(res_bytes)
return result
def scoped(self, scope: str) -> "DiskCache":
return DiskCache(self.config, self._cache, prefix=f"{self._prefix}/{scope}/")
def __contains__(self, key: str) -> bool:
return key in self._cache
@staticmethod
def make_json_key(d: dict[str, Any]) -> str:
# Keep the key human-readable at the cost of some cache size and performance.
return json.dumps(d, sort_keys=True)
def close(self) -> None:
self._cache.close()
def invalidate_tag(self, tag: str) -> int:
n_evicted: int = self._cache.evict(tag=tag)
return n_evicted