54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
|
|
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
|