Files
anonymisation/gui_v6/single_instance.py

72 lines
2.3 KiB
Python

"""Protection multi-instance de la GUI V6 (P0-7).
- Windows (frozen) : mutex nommé kernel via ctypes — ce nom DEVRA être déclaré comme
``AppMutex`` dans installer/Anonymisation.iss (Plan 3 / D8) pour que l'installeur
ferme l'app avant une mise à jour.
- POSIX (dev/test) : verrou ``fcntl`` exclusif sur un fichier dans le dossier app.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
# Nom destiné à l'AppMutex de installer/Anonymisation.iss (Plan 3 / D8). NE PAS modifier sans synchroniser le .iss.
APP_MUTEX_NAME = "AivanonymAnonymisationV6"
class AlreadyRunningError(RuntimeError):
"""Une autre instance de l'application est déjà en cours d'exécution."""
def _lock_dir() -> Path:
base = os.environ.get("LOCALAPPDATA")
root = Path(base) if base else Path.home() / ".local" / "share"
d = root / "Aivanov" / "Anonymisation"
d.mkdir(parents=True, exist_ok=True)
return d
class SingleInstance:
def __init__(self) -> None:
self._handle = None # mutex Windows
self._fh = None # file handle POSIX
def acquire(self) -> None:
if sys.platform.startswith("win"):
self._acquire_windows()
else:
self._acquire_posix()
def _acquire_windows(self) -> None: # pragma: no cover (exécuté sur Windows)
import ctypes
ERROR_ALREADY_EXISTS = 183
handle = ctypes.windll.kernel32.CreateMutexW(None, False, APP_MUTEX_NAME)
if not handle or ctypes.windll.kernel32.GetLastError() == ERROR_ALREADY_EXISTS:
raise AlreadyRunningError("L'application est déjà ouverte.")
self._handle = handle
def _acquire_posix(self) -> None:
import fcntl
path = _lock_dir() / "instance.lock"
fh = open(path, "w")
try:
fcntl.flock(fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError:
fh.close()
raise AlreadyRunningError("L'application est déjà ouverte.")
self._fh = fh
def release(self) -> None:
if self._handle is not None: # pragma: no cover
import ctypes
ctypes.windll.kernel32.CloseHandle(self._handle)
self._handle = None
if self._fh is not None:
self._fh.close()
self._fh = None