From a53a2568ec6e4e53d32a48443f50eee9d9fb8fcd Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:49:01 +1000 Subject: scheme: fix not saving atomically Causes programs which rely on the save file (e.g. the shell) to fail occasionally as they try to read while the cli is writing --- src/caelestia/utils/paths.py | 10 ++++++++++ src/caelestia/utils/scheme.py | 23 +++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/caelestia/utils/paths.py b/src/caelestia/utils/paths.py index 6a6e0a8..3b5a7a6 100644 --- a/src/caelestia/utils/paths.py +++ b/src/caelestia/utils/paths.py @@ -1,5 +1,8 @@ import hashlib +import json import os +import shutil +import tempfile from pathlib import Path config_dir = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) @@ -31,3 +34,10 @@ def compute_hash(path: str) -> str: sha.update(chunk) return sha.hexdigest() + + +def atomic_dump(path: Path, content: dict[str, any]) -> None: + with tempfile.NamedTemporaryFile("w") as f: + json.dump(content, f) + f.flush() + shutil.move(f.name, path) diff --git a/src/caelestia/utils/scheme.py b/src/caelestia/utils/scheme.py index c978231..9027589 100644 --- a/src/caelestia/utils/scheme.py +++ b/src/caelestia/utils/scheme.py @@ -3,7 +3,7 @@ import random from pathlib import Path from caelestia.utils.material import get_colours_for_image -from caelestia.utils.paths import scheme_data_dir, scheme_path +from caelestia.utils.paths import atomic_dump, scheme_data_dir, scheme_path class Scheme: @@ -100,17 +100,16 @@ class Scheme: def save(self) -> None: scheme_path.parent.mkdir(parents=True, exist_ok=True) - with scheme_path.open("w") as f: - json.dump( - { - "name": self.name, - "flavour": self.flavour, - "mode": self.mode, - "variant": self.variant, - "colours": self.colours, - }, - f, - ) + atomic_dump( + scheme_path, + { + "name": self.name, + "flavour": self.flavour, + "mode": self.mode, + "variant": self.variant, + "colours": self.colours, + }, + ) def set_random(self) -> None: self._name = random.choice(get_scheme_names()) -- cgit v1.2.3-freya