Refactor radar system
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""Atomic writes for JSON settings shared by GUI and pipeline processes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from contextlib import suppress
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
|
||||
def atomic_write_json(path: Path, payload: object, *, durable: bool = False) -> Path:
|
||||
"""Serialize strict JSON before opening any files, then atomically commit it."""
|
||||
return atomic_write_text(path, json.dumps(payload, indent=2, allow_nan=False), durable=durable)
|
||||
|
||||
|
||||
def atomic_write_text(path: Path, text: str, *, durable: bool = False) -> Path:
|
||||
"""Replace a text file only after a complete write, cleaning up failed writes.
|
||||
|
||||
Each call owns its temporary file, so concurrent writers cannot truncate or
|
||||
rename one another's work. Durable writes also sync the file and directory;
|
||||
frequent live settings can omit those syncs to avoid unnecessary disk I/O.
|
||||
"""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
temporary = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", encoding="utf-8", dir=path.parent,
|
||||
prefix=f".{path.name}.", suffix=".tmp", delete=False,
|
||||
) as handle:
|
||||
temporary = Path(handle.name)
|
||||
handle.write(text)
|
||||
if durable:
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
os.replace(temporary, path)
|
||||
if durable:
|
||||
directory_fd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
|
||||
try:
|
||||
os.fsync(directory_fd)
|
||||
finally:
|
||||
os.close(directory_fd)
|
||||
finally:
|
||||
if temporary is not None:
|
||||
with suppress(OSError):
|
||||
temporary.unlink(missing_ok=True)
|
||||
return path
|
||||
Reference in New Issue
Block a user