diff --git a/src/specify_cli/events.py b/src/specify_cli/events.py index d3002fe805..cbc0d1db4a 100644 --- a/src/specify_cli/events.py +++ b/src/specify_cli/events.py @@ -17,6 +17,7 @@ import sys import subprocess import platform +import tempfile from pathlib import Path from typing import TYPE_CHECKING, Any @@ -2010,10 +2011,23 @@ def _load_user_json(path: Path) -> dict | None: def _safe_write_json(dst: Path, data: dict) -> None: - """Write *data* as JSON to *dst* after validating the destination (#12).""" + """Write *data* as JSON to *dst* atomically after validating the destination (#12).""" _ensure_safe_destination(dst) dst.parent.mkdir(parents=True, exist_ok=True) - dst.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") + fd, tmp = tempfile.mkstemp( + dir=str(dst.parent), prefix=f".{dst.name}.", suffix=".tmp" + ) + try: + with os.fdopen(fd, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + f.write("\n") + os.replace(tmp, dst) + except BaseException: + try: + os.unlink(tmp) + except OSError: + pass + raise def _ensure_safe_destination(dst: Path) -> None: