from __future__ import annotations import json import os import tempfile from typing import Any, Optional, Tuple def cache_dir() -> str: """Directory where the scheduler owner writes shared cache JSON files.""" d = os.environ.get("LAIR_CACHE_DIR", "/tmp/lair-cache") os.makedirs(d, exist_ok=True) return d def cache_file(name: str) -> str: return os.path.join(cache_dir(), f"{name}.json") def atomic_write_json(path: str, payload: dict) -> None: """Write JSON atomically so readers never observe partial content.""" parent = os.path.dirname(path) or "." fd, tmp = tempfile.mkstemp(prefix=".tmp-", dir=parent) try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(payload, f, ensure_ascii=False, separators=(",", ":")) os.replace(tmp, path) finally: try: if os.path.exists(tmp): os.unlink(tmp) except Exception: pass def load_json_if_newer(path: str, last_mtime: float) -> Tuple[Optional[dict], float]: """Load JSON only if the file exists and has mtime newer than last_mtime.""" try: st = os.stat(path) except FileNotFoundError: return None, last_mtime except Exception: return None, last_mtime mtime = float(st.st_mtime) if mtime <= float(last_mtime): return None, last_mtime try: with open(path, "r", encoding="utf-8") as f: return json.load(f), mtime except Exception: return None, last_mtime