"""Shared geolocation singleton. Runs backend/geolocate.py (IP geolocation, cached) exactly once and notifies subscribers. Both the Location map and the Weather widget subscribe, so there is a single source of truth for "where am I" and only one network lookup. """ from __future__ import annotations import sys from typing import Callable, Optional from lib.proc import run_json from paths import BACKEND_DIR class LocationService: def __init__(self) -> None: self.data: Optional[dict] = None self._subs: list[Callable[[dict], None]] = [] self._inflight = False def subscribe(self, cb: Callable[[dict], None]) -> None: self._subs.append(cb) if self.data is not None: cb(self.data) def get(self) -> Optional[dict]: if self.data is None and not self._inflight: self.refresh() return self.data def refresh(self) -> None: if self._inflight: return self._inflight = True argv = [sys.executable, str(BACKEND_DIR / "geolocate.py")] run_json(argv, self._on_result) def _on_result(self, ok: bool, data) -> None: self._inflight = False if ok and isinstance(data, dict) and "lat" in data: self.data = data for cb in list(self._subs): cb(data) _instance: LocationService | None = None def get_location_service() -> LocationService: global _instance if _instance is None: _instance = LocationService() return _instance