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

1"""Time-based filesystem cache for expensive preflight checks. 

2 

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""" 

8 

9from __future__ import annotations 

10 

11import hashlib 

12import logging 

13import time 

14from pathlib import Path 

15 

16logger = logging.getLogger(__name__) 

17 

18 

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" 

24 

25 

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 

39 

40 

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)