MCPcopy Create free account
hub / github.com/VitoHowe/glm-coding / JsonFileStore

Class JsonFileStore

app/storage/json_store.py:18–82  ·  view source on GitHub ↗

Thread-safe JSON file persistence with atomic writes.

Source from the content-addressed store, hash-verified

16
17
18class JsonFileStore:
19 """Thread-safe JSON file persistence with atomic writes."""
20
21 _replace_retry_delays = (0.05, 0.1, 0.2, 0.35, 0.5)
22
23 def __init__(self, path: Path, default_factory: Callable[[], Any]) -> None:
24 self.path = path
25 self.default_factory = default_factory
26 self._lock = threading.Lock()
27
28 def read(self) -> Any:
29 with self._lock:
30 return self._read_unlocked()
31
32 def write(self, payload: Any) -> Any:
33 with self._lock:
34 self._write_unlocked(payload)
35 return payload
36
37 def update(self, updater: Callable[[Any], Any]) -> Any:
38 with self._lock:
39 current = self._read_unlocked()
40 updated = updater(current)
41 self._write_unlocked(updated)
42 return updated
43
44 def _read_unlocked(self) -> Any:
45 if not self.path.exists():
46 return copy.deepcopy(self.default_factory())
47
48 raw = self.path.read_bytes()
49 if not raw.strip():
50 return copy.deepcopy(self.default_factory())
51 if orjson is not None:
52 return orjson.loads(raw)
53 return json.loads(raw.decode("utf-8"))
54
55 def _write_unlocked(self, payload: Any) -> None:
56 self.path.parent.mkdir(parents=True, exist_ok=True)
57 temp_path = self.path.with_suffix(f"{self.path.suffix}.tmp")
58 if orjson is not None:
59 serialized = orjson.dumps(payload, option=orjson.OPT_INDENT_2) + b"\n"
60 temp_path.write_bytes(serialized)
61 else: # pragma: no cover - only used without orjson
62 temp_path.write_text(
63 json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
64 encoding="utf-8",
65 )
66 self._replace_with_retry(temp_path)
67
68 def _replace_with_retry(self, temp_path: Path) -> None:
69 last_error: PermissionError | None = None
70 for delay in (*self._replace_retry_delays, 0):
71 try:
72 temp_path.replace(self.path)
73 return
74 except PermissionError as exc:
75 last_error = exc

Callers 4

__init__Method · 0.90
__init__Method · 0.90
load_sessionMethod · 0.90
save_sessionMethod · 0.90

Calls

no outgoing calls

Tested by

no test coverage detected