"""Real Zinc-backed control plane for DITAv2.""" from __future__ import annotations import json import struct import sys from pathlib import Path from typing import Any, Dict, Optional from .control import BackendMode, ControlPlane, ControlUpdate, KernelControlSnapshot, KernelMode, KernelVerbosity _ZINC_ADAPTER_PATH = Path(__file__).resolve().parents[3] / "zinc" / "adapters" / "python" if _ZINC_ADAPTER_PATH.exists() and str(_ZINC_ADAPTER_PATH) not in sys.path: sys.path.insert(0, str(_ZINC_ADAPTER_PATH)) try: # pragma: no cover - exercised in integration tests from zinc import SharedRegion except Exception as exc: # pragma: no cover SharedRegion = None # type: ignore[assignment] _ZINC_IMPORT_ERROR = exc else: _ZINC_IMPORT_ERROR = None class RealZincUnavailable(RuntimeError): """Raised when the Zinc Python adapter cannot be loaded.""" def require_real_zinc() -> None: if SharedRegion is None: raise RealZincUnavailable(str(_ZINC_IMPORT_ERROR)) def _json_default(value: Any) -> Any: if hasattr(value, "value"): return value.value if hasattr(value, "isoformat"): try: return value.isoformat() except Exception: pass if hasattr(value, "__dict__"): return dict(vars(value)) raise TypeError(f"Unsupported value: {type(value)!r}") def _encode_packet(seq: int, payload: Dict[str, Any]) -> bytes: text = json.dumps(payload, sort_keys=True, ensure_ascii=False, default=_json_default, separators=(",", ":")).encode("utf-8") return struct.pack("!QQ", int(seq), len(text)) + text def _decode_packet(buf: memoryview) -> Dict[str, Any]: if len(buf) < 16: return {} seq, size = struct.unpack_from("!QQ", buf, 0) if size <= 0 or size > len(buf) - 16: return {} payload = bytes(buf[16 : 16 + size]).decode("utf-8") out = json.loads(payload) if isinstance(out, dict): out["_seq"] = seq return out class RealZincControlPlane(ControlPlane): """Shared-memory Zinc-backed control plane.""" def __init__(self, *, prefix: str, create: bool = True) -> None: require_real_zinc() base = prefix.strip("/").replace("/", "_") self.region_name = f"{base}_control" self._seq = 0 self._snapshot = KernelControlSnapshot() if create: self.region = SharedRegion.create(self.region_name, 1 << 20) self._write_region(self._seq, self._snapshot.as_dict()) else: self.region = SharedRegion.open(self.region_name) payload = _decode_packet(self.region.as_buffer()) control = payload.get("control") if isinstance(payload, dict) else None if isinstance(control, dict): self._snapshot = KernelControlSnapshot(**control) def close(self) -> None: self.region.close() def read(self) -> KernelControlSnapshot: payload = _decode_packet(self.region.as_buffer()) control = payload.get("control") if isinstance(payload, dict) else None if not isinstance(control, dict): return self._snapshot self._snapshot = KernelControlSnapshot(**control) return self._snapshot def update(self, update: ControlUpdate) -> KernelControlSnapshot: self._snapshot = update.apply(self.read()) self._seq += 1 self._write_region(self._seq, self._snapshot.as_dict()) return self._snapshot def mirror(self) -> Dict[str, Any]: return self._snapshot.as_dict() def wait(self, timeout_ms: int = 1000) -> bool: try: return bool(self.region.wait(timeout_ms)) except Exception: return False def notify(self) -> None: try: self.region.notify() except Exception: pass def _write_region(self, seq: int, control: Dict[str, Any]) -> None: packet = _encode_packet(seq, {"control": control}) buf = self.region.as_buffer() if len(packet) > len(buf): raise ValueError(f"payload too large for Zinc control region: {len(packet)} > {len(buf)}") view = memoryview(buf) view[: len(packet)] = packet if len(view) > len(packet): view[len(packet) :] = b"\x00" * (len(view) - len(packet)) try: self.region.notify() except Exception: pass