Standardisation 3

This commit is contained in:
2026-08-20 14:35:04 +02:00
parent 63f690b353
commit 8b087bb3e4
9 changed files with 1187 additions and 683 deletions
+43
View File
@@ -0,0 +1,43 @@
from __future__ import annotations
import shutil
import uuid
from contextlib import contextmanager
from pathlib import Path
def _remove_path(path: Path) -> None:
if path.is_symlink() or path.is_file():
path.unlink()
elif path.exists():
shutil.rmtree(path)
@contextmanager
def staged_directory(destination: str | Path):
"""Build a directory beside its destination and replace on success."""
target = Path(destination)
target.parent.mkdir(parents=True, exist_ok=True)
token = uuid.uuid4().hex
staging = target.with_name(f".{target.name}.{token}.tmp")
backup = target.with_name(f".{target.name}.{token}.backup")
staging.mkdir()
committed = False
try:
yield staging
if target.exists():
target.replace(backup)
try:
staging.replace(target)
committed = True
except Exception:
if backup.exists() and not target.exists():
backup.replace(target)
raise
if backup.exists():
_remove_path(backup)
finally:
if staging.exists():
_remove_path(staging)
if not committed and backup.exists() and not target.exists():
backup.replace(target)