from __future__ import annotations from pathlib import Path try: import fcntl except ImportError: # pragma: no cover - exercised by Windows validation fcntl = None import msvcrt class MaintenanceLock: """Process fence used only by fixture and later approved activation executors.""" def __init__(self, path: Path) -> None: self.path = path self._handle = None def __enter__(self) -> "MaintenanceLock": self.path.parent.mkdir(parents=True, exist_ok=True) self._handle = self.path.open("a+") if fcntl is not None: try: fcntl.flock(self._handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except BlockingIOError: self._handle.close() self._handle = None raise else: self._handle.seek(0) try: msvcrt.locking(self._handle.fileno(), msvcrt.LK_NBLCK, 1) except OSError as exc: self._handle.close() self._handle = None raise BlockingIOError("maintenance lock held") from exc return self def __exit__(self, exc_type, exc, tb) -> None: if self._handle is not None: if fcntl is not None: fcntl.flock(self._handle.fileno(), fcntl.LOCK_UN) else: self._handle.seek(0) msvcrt.locking(self._handle.fileno(), msvcrt.LK_UNLCK, 1) self._handle.close() self._handle = None