| """Disk cache for macro expansion results — keyed by content hash.""" |
|
|
| from __future__ import annotations |
|
|
| import hashlib |
| import json |
| from pathlib import Path |
|
|
| CACHE_DIR = Path("data/cache/expansions") |
|
|
|
|
| class ExpansionCache: |
| def __init__(self, cache_dir: Path | None = None): |
| self.cache_dir = cache_dir or CACHE_DIR |
| self.cache_dir.mkdir(parents=True, exist_ok=True) |
|
|
| def _key(self, library: str, macro_name: str, call_form: str) -> str: |
| content = f"{library}:{macro_name}:{call_form}" |
| return hashlib.sha256(content.encode()).hexdigest()[:16] |
|
|
| def get(self, library: str, macro_name: str, call_form: str) -> dict | None: |
| key = self._key(library, macro_name, call_form) |
| path = self.cache_dir / f"{key}.json" |
| if path.exists(): |
| try: |
| return json.loads(path.read_text()) |
| except json.JSONDecodeError: |
| return None |
| return None |
|
|
| def put(self, library: str, macro_name: str, call_form: str, result: dict) -> None: |
| key = self._key(library, macro_name, call_form) |
| path = self.cache_dir / f"{key}.json" |
| path.write_text(json.dumps(result)) |
|
|
| def invalidate_library(self, library: str) -> None: |
| prefix = hashlib.sha256(f"{library}:".encode()).hexdigest()[:8] |
| for p in self.cache_dir.glob(f"{prefix}*.json"): |
| p.unlink(missing_ok=True) |
|
|