some fixes again

This commit is contained in:
Ayzen
2026-06-05 17:50:59 +03:00
parent bbea744459
commit 3c30a12d4a
24 changed files with 209 additions and 157 deletions
+17 -11
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
from contextlib import suppress
import json
import os
from pathlib import Path
@@ -42,13 +43,12 @@ class ConfigWriter:
asset.bundle_path = str(bundle_path)
def write(self, config: RunConfigModel, output_path: Path) -> Path:
"""Atomically write run configuration JSON file.
"""Atomically write the run configuration JSON file.
Mirrors ProcessingLiveConfigWriter: dump to a sibling .tmp, flush+fsync to
durably commit the bytes, then os.replace() onto the destination. The replace
is atomic, so a C++ consumer can never observe a half-written config (which
would abort it with an opaque JSON parse error), even across a crash or power
loss mid-write on the SD-card-backed Pi.
Dump to a sibling ``.tmp``, flush + ``fsync`` to durably commit the bytes on
the SD-card-backed Pi, then ``os.replace()`` atomically onto the destination.
A C++ consumer can therefore never observe a half-written config (which would
abort it with an opaque JSON parse error), even across a crash or power loss.
"""
output_path.parent.mkdir(parents=True, exist_ok=True)
# allow_nan=False: a stray NaN/Infinity must fail loudly here in Python
@@ -56,11 +56,17 @@ class ConfigWriter:
# consumer at startup with an opaque JSON parse error.
serialized = json.dumps(config.to_dict(), indent=2, allow_nan=False)
tmp_path = output_path.with_suffix(output_path.suffix + ".tmp")
with open(tmp_path, "w", encoding="utf-8") as handle:
handle.write(serialized)
handle.flush()
os.fsync(handle.fileno())
os.replace(tmp_path, output_path)
try:
with open(tmp_path, "w", encoding="utf-8") as handle:
handle.write(serialized)
handle.flush()
os.fsync(handle.fileno())
os.replace(tmp_path, output_path)
except Exception:
# Never leave a half-written .tmp behind on a write/fsync failure.
with suppress(OSError):
tmp_path.unlink()
raise
return output_path