ruvnet/RuView · error · ImportError

SensingClient requires the `websockets` package. Install wit

Error message

SensingClient requires the `websockets` package. Install with `pip install "wifi-densepose[client]"` to enable the client extras.

What it means

SensingClient.__init__ raises ImportError when the optional `websockets` package is missing. The module-level import is guarded via _WEBSOCKETS_AVAILABLE so importing wifi_densepose.client.ws succeeds regardless; the failure is deferred to construction time with instructions to install the `client` extra.

Source

Thrown at python/wifi_densepose/client/ws.py:221

    automation vs "fail fast" for a CLI tool that should exit).

    Auth: pass ``token=`` to send ``Authorization: Bearer <token>`` on
    the WS upgrade, for sensing-servers started with ``RUVIEW_API_TOKEN``
    set. If ``token`` is omitted it defaults to the ``RUVIEW_API_TOKEN``
    environment variable; when neither is set, no header is sent.
    """

    def __init__(
        self,
        url: str,
        *,
        token: Optional[str] = None,
        ping_interval: float = 20.0,
        ping_timeout: float = 20.0,
        max_size: int = 16 * 1024 * 1024,
    ) -> None:
        if not _WEBSOCKETS_AVAILABLE:
            raise ImportError(
                "SensingClient requires the `websockets` package. Install with "
                "`pip install \"wifi-densepose[client]\"` to enable the client extras."
            )
        self.url = url
        # Bearer token for auth-enabled sensing-servers. Explicit
        # constructor argument wins; otherwise fall back to the
        # RUVIEW_API_TOKEN environment variable. An empty value (unset
        # env, or "") means "no auth" — no Authorization header is sent.
        self._token = token if token is not None else os.environ.get(TOKEN_ENV_VAR)
        self._ping_interval = ping_interval
        self._ping_timeout = ping_timeout
        self._max_size = max_size
        self._ws: Any = None  # websockets.WebSocketClientProtocol — typed Any to avoid import cost

    async def __aenter__(self) -> "SensingClient":
        connect_kwargs: dict[str, Any] = dict(
            ping_interval=self._ping_interval,
            ping_timeout=self._ping_timeout,

View on GitHub (pinned to 4685618388)

Solutions

  1. Install the extra: pip install "wifi-densepose[client]"
  2. Or install directly: pip install websockets (>=12; the client adapts to the 13/14 header-kwarg rename by signature inspection)
  3. Add the extra to requirements so every environment and lockfile includes it

Example fix

# before
pip install wifi-densepose

# after
pip install "wifi-densepose[client]"
Defensive patterns

Strategy: validation

Validate before calling

import importlib.util

if importlib.util.find_spec("websockets") is None:
    raise SystemExit(
        "websockets missing; run: pip install 'wifi-densepose[client]'"
    )
from wifi_densepose.client import SensingClient

Try / catch

try:
    client = SensingClient(url)
except ImportError as e:
    log.warning("WS ingestion disabled: %s", e)
    client = None

Prevention

When it happens

Trigger: Constructing `SensingClient("ws://host:8765/ws/sensing")` where the base wifi-densepose wheel is installed without the `[client]` extra and websockets (>=12) is absent.

Common situations: Slim production images; environments installed from a bare `pip install wifi-densepose`; dependency graphs that strip extras.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/11dc10c947b07543. Report an issue: GitHub.