"""Conditional remote debugging via ``debugpy``. **Default (``DEBUGPY`` unset or ``0``):** ``debugpy`` is *never imported* — zero overhead, production-safe. **``DEBUGPY=1``:** imports ``debugpy`` and opens a *non-blocking* listener on ``0.0.0.0:5678`` (override with ``DEBUGPY_PORT``). The application continues immediately; an IDE (VS Code / PyCharm) attaches on demand at any time. Usage: call :func:`configure_debugging` once, as early as possible in the entrypoint (see ``app/main.py``). """ from __future__ import annotations import contextlib import logging import os from typing import Any logger = logging.getLogger("app.debugging") _listener: Any = None # debugpy.listen() result, when enabled def _port() -> int: try: return int(os.environ.get("DEBUGPY_PORT", "5678")) except ValueError: return 5678 def _is_enabled() -> bool: return os.environ.get("DEBUGPY", "0").strip() == "1" def configure_debugging() -> bool: """Enable debugpy if and only if ``DEBUGPY=1``. Returns ``True`` when the listener was started. """ if not _is_enabled(): return False global _listener import debugpy # imported ONLY when explicitly enabled — no overhead otherwise port = _port() _listener = debugpy.listen(("0.0.0.0", port)) logger.warning( "debugpy: remote debugging ENABLED, listening on 0.0.0.0:%d (attach on demand)", port ) return True def shutdown_debugpy() -> None: """Stop the debugpy listener (used by tests and graceful shutdown).""" global _listener if _listener is not None: sock = getattr(_listener, "local_socket", None) if sock is not None: with contextlib.suppress(OSError): sock.close() _listener = None