Source code for xpcsviewer.utils.atomic_io

"""Atomic file I/O utilities.

Provides ``safe_json_write`` which writes JSON data to a temporary file
then atomically replaces the target — preventing data corruption if the
process is interrupted mid-write.
"""

import json
import logging
import os
import tempfile
from pathlib import Path

logger = logging.getLogger(__name__)


[docs] def safe_json_write(path: str | Path, data: dict, *, indent: int = 2) -> None: """Write *data* as JSON to *path* atomically. Creates the parent directory if needed, writes to a temporary file in the same directory, then calls :func:`os.replace` to swap the temp file into place (atomic on POSIX). Parameters ---------- path : str | Path Destination file path. data : dict JSON-serialisable dictionary. indent : int JSON indentation (default 2). Raises ------ OSError If the write or replace fails. TypeError If *data* is not JSON-serialisable. """ path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) fd, tmp_path = tempfile.mkstemp(dir=path.parent, prefix=".tmp_", suffix=".json") try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, indent=indent) os.replace(tmp_path, path) except BaseException: # Clean up the temp file on any failure try: os.unlink(tmp_path) except OSError: pass raise