Persist user prefs atomically (#1840)

This commit is contained in:
.bulat
2026-06-04 05:55:22 +03:00
committed by GitHub
parent ceb62385f1
commit e340674c12
2 changed files with 53 additions and 2 deletions

View File

@@ -19,9 +19,13 @@ def _load():
def _save(prefs): def _save(prefs):
os.makedirs(os.path.dirname(PREFS_FILE), exist_ok=True) os.makedirs(os.path.dirname(PREFS_FILE) or ".", exist_ok=True)
with open(PREFS_FILE, "w", encoding="utf-8") as f: tmp = f"{PREFS_FILE}.tmp.{os.getpid()}"
with open(tmp, "w", encoding="utf-8") as f:
json.dump(prefs, f, indent=2) json.dump(prefs, f, indent=2)
f.flush()
os.fsync(f.fileno())
os.replace(tmp, PREFS_FILE)
def _load_for_user(user: Optional[str] = None) -> dict: def _load_for_user(user: Optional[str] = None) -> dict:

View File

@@ -0,0 +1,47 @@
import json
import routes.prefs_routes as prefs_routes
def test_save_replaces_prefs_file_atomically(monkeypatch, tmp_path):
calls = []
real_replace = prefs_routes.os.replace
def fake_replace(src, dst):
calls.append((src, dst))
real_replace(src, dst)
prefs_file = tmp_path / "data" / "user_prefs.json"
monkeypatch.setattr(prefs_routes, "PREFS_FILE", str(prefs_file))
monkeypatch.setattr(prefs_routes.os, "replace", fake_replace)
prefs_routes._save({"theme": "dark"})
assert len(calls) == 1
src, dst = calls[0]
assert dst == str(prefs_file)
assert src.startswith(str(prefs_file) + ".tmp.")
assert json.loads(prefs_file.read_text(encoding="utf-8")) == {"theme": "dark"}
assert not list(prefs_file.parent.glob("*.tmp.*"))
def test_save_for_user_preserves_scoped_user_prefs(monkeypatch, tmp_path):
prefs_file = tmp_path / "data" / "user_prefs.json"
monkeypatch.setattr(prefs_routes, "PREFS_FILE", str(prefs_file))
prefs_routes._save_for_user("alice", {"theme": "dark"})
data = json.loads(prefs_file.read_text(encoding="utf-8"))
assert data == {"_users": {"alice": {"theme": "dark"}}}
assert prefs_routes._load_for_user("alice") == {"theme": "dark"}
def test_save_for_user_preserves_flat_prefs_when_auth_disabled(monkeypatch, tmp_path):
prefs_file = tmp_path / "data" / "user_prefs.json"
monkeypatch.setattr(prefs_routes, "PREFS_FILE", str(prefs_file))
prefs_routes._save_for_user(None, {"theme": "dark"})
data = json.loads(prefs_file.read_text(encoding="utf-8"))
assert data == {"theme": "dark"}
assert prefs_routes._load_for_user(None) == {"theme": "dark"}