File size: 1,448 Bytes
bc50ea0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | """Durable publication primitives for deferred processing outputs."""
import errno
import json
import os
from pathlib import Path
import uuid
def sync_file(path):
with open(path, "rb") as handle:
os.fsync(handle.fileno())
def sync_directory(path):
# Windows has no portable directory fsync. Some network filesystems also
# explicitly don't implement it; real I/O failures must still propagate.
if os.name == "nt":
return
try:
fd = os.open(path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
try:
os.fsync(fd)
finally:
os.close(fd)
except OSError as exc:
if exc.errno not in (errno.EINVAL, errno.ENOSYS, errno.EOPNOTSUPP):
raise
def atomic_json(path, value):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + "." + uuid.uuid4().hex + ".tmp")
try:
with temporary.open("x", encoding="utf-8") as handle:
json.dump(value, handle, ensure_ascii=False, indent=2, sort_keys=True)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
sync_directory(path.parent)
finally:
# A failed post-rename fsync is an uncertain commit, not permission to
# delete the newly published document or its referenced media.
temporary.unlink(missing_ok=True)
|