"""Add an OSM basemap with a SIGALRM-based timeout (Unix only).""" from __future__ import annotations import signal from pathlib import Path def add_basemap_with_timeout( ax: "Axes", zoom: int = 9, cache_dir: "str | Path | None" = None, timeout_s: float = 15.0, ) -> bool: """Add an OpenStreetMap basemap with a hard timeout (Unix only). Uses SIGALRM to enforce a wall-clock timeout on the tile fetch. If the download does not complete within ``timeout_s`` seconds the function returns False without modifying the Axes further. **Unix only** — SIGALRM is not available on Windows. On Windows this function always returns False immediately. Args: ax: matplotlib Axes with projected extent (EPSG:3857). zoom: Tile zoom level (1–19). cache_dir: Optional tile cache directory. timeout_s: Maximum seconds to wait for tiles. Default 15.0. Returns: True if the basemap was added successfully; False on timeout or error. """ if not hasattr(signal, "SIGALRM"): # Windows — SIGALRM not available return False try: import contextily as ctx # type: ignore except ImportError: return False def _handler(signum: int, frame: object) -> None: # noqa: ARG001 raise TimeoutError("basemap fetch timed out") old_handler = signal.signal(signal.SIGALRM, _handler) signal.setitimer(signal.ITIMER_REAL, timeout_s) try: if cache_dir is not None: ctx.set_cache_dir(str(cache_dir)) ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik, zoom=zoom) return True except Exception: # noqa: BLE001 return False finally: signal.setitimer(signal.ITIMER_REAL, 0) signal.signal(signal.SIGALRM, old_handler)