Coverage for src/ai_shell/cache.py: 72%
29 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-05 22:06 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-05 22:06 +0000
1"""Time-based filesystem cache for expensive preflight checks.
3Used to avoid repeating slow operations (Docker image pulls, Bedrock auth
4probes) on every launch. Cache entries live under
5``~/.cache/ai-shell/<namespace>/`` as small timestamp files keyed by a hash
6of caller-provided strings.
7"""
9from __future__ import annotations
11import hashlib
12import logging
13import time
14from pathlib import Path
16logger = logging.getLogger(__name__)
19def _cache_path(namespace: str, key: str) -> Path:
20 digest = hashlib.sha256(key.encode("utf-8"), usedforsecurity=False).hexdigest()[:16]
21 base = Path.home() / ".cache" / "ai-shell" / namespace
22 base.mkdir(parents=True, exist_ok=True)
23 return base / f"{digest}.timestamp"
26def is_fresh(namespace: str, key: str, ttl_seconds: int) -> bool:
27 """Return True if a cache entry exists for *key* and is younger than the TTL."""
28 if ttl_seconds <= 0:
29 return False
30 path = _cache_path(namespace, key)
31 if not path.is_file():
32 return False
33 try:
34 last = float(path.read_text().strip())
35 except (OSError, ValueError):
36 return False
37 age = time.time() - last
38 return age < ttl_seconds
41def mark_fresh(namespace: str, key: str) -> None:
42 """Stamp the cache entry for *key* with the current time."""
43 path = _cache_path(namespace, key)
44 try:
45 path.write_text(f"{time.time()}\n")
46 except OSError as e:
47 logger.debug("Failed to write cache marker %s: %s", path, e)