Skip to content

Connection

Runnable example

python/examples/connection_ping.py — calls Connection.Ping once and prints the round-trip time and server timestamp.

resoio.connection.ConnectionClient

ConnectionClient(socket_path: str | None = None)

Bases: _BaseClient[ConnectionStub]

Async client for the Resonite IO Connection service over a UDS.

Use as an async context manager so the gRPC channel is closed deterministically. With socket_path=None the path is resolved on __aenter__ via RESONITE_IO_SOCKETRESONITE_IO_SOCKET_DIR~/.resonite-io/; resolution may raise :class:SocketNotFoundError or :class:AmbiguousSocketError.

Source code in src/resoio/_client.py
def __init__(self, socket_path: str | None = None) -> None:
    # Defer resolution to __aenter__ so env vars patched between
    # construction and connection are honoured, and so resolution
    # errors surface at the connect site.
    self._explicit_path: str | None = socket_path
    self._channel: Channel | None = None
    self._stub: TStub | None = None
    self._resolved_path: str | None = None

ping async

ping(message: str) -> PingResponse

Round-trip a liveness check and return the server's echo.

A healthy mod echoes message back unchanged, so callers can use the round trip as a connectivity probe before opening other modality streams.

RETURNS DESCRIPTION
The

class:PingResponse carrying the echoed message and

TYPE: PingResponse

PingResponse

the server-side response timestamp.

RAISES DESCRIPTION
GRPCError

The RPC failed at the transport or server layer (e.g. the mod went away mid-call).

Source code in src/resoio/connection.py
async def ping(self, message: str) -> PingResponse:
    """Round-trip a liveness check and return the server's echo.

    A healthy mod echoes ``message`` back unchanged, so callers can use
    the round trip as a connectivity probe before opening other
    modality streams.

    Returns:
        The :class:`PingResponse` carrying the echoed ``message`` and
        the server-side response timestamp.

    Raises:
        grpclib.exceptions.GRPCError: The RPC failed at the transport
            or server layer (e.g. the mod went away mid-call).
    """
    stub = self._require_stub()
    return await stub.ping(PingRequest(message=message))

resoio.connection.wait_for_ready async

wait_for_ready(
    socket_path: str | None = None,
    *,
    timeout: float | None = None,
    interval: float = 0.1,
) -> str

Block until the Resonite IO server answers Connection.Ping.

The startup-readiness gate: during cold boot the socket may be absent, bound with no listener yet, or bound by an engine that has not finished initialising. Each of those is retried; a healthy ping ends the wait and the resolved UDS path is returned.

With socket_path=None the path is re-resolved on every attempt via the usual env order (RESONITE_IO_SOCKETRESONITE_IO_SOCKET_DIR~/.resonite-io/), so a socket that appears mid-wait is picked up and stale sockets whose owning engine PID is gone are skipped by the resolver. Connecting directly (bypassing the modality client) keeps the poll from firing the once-per-process version-mismatch probe each round.

PARAMETER DESCRIPTION
socket_path

Explicit UDS path to wait on, or None to resolve it on each attempt.

TYPE: str | None DEFAULT: None

timeout

Maximum seconds to wait. None waits indefinitely; <= 0 makes a single attempt.

TYPE: float | None DEFAULT: None

interval

Seconds to sleep between attempts (clamped so the wait never overshoots timeout).

TYPE: float DEFAULT: 0.1

RETURNS DESCRIPTION
str

The resolved UDS path that answered the ping.

RAISES DESCRIPTION
TimeoutError

timeout elapsed before a ping succeeded.

AmbiguousSocketError

Multiple live sockets matched and no explicit path was given — waiting cannot disambiguate, so it propagates.

GRPCError

The server answered with a non-retryable status. FAILED_PRECONDITION means "not ready yet" and is retried; every other status propagates.

Source code in src/resoio/connection.py
async def wait_for_ready(
    socket_path: str | None = None,
    *,
    timeout: float | None = None,
    interval: float = 0.1,
) -> str:
    """Block until the Resonite IO server answers ``Connection.Ping``.

    The startup-readiness gate: during cold boot the socket may be absent,
    bound with no listener yet, or bound by an engine that has not finished
    initialising. Each of those is retried; a healthy ping ends the wait and
    the resolved UDS path is returned.

    With ``socket_path=None`` the path is re-resolved on every attempt via
    the usual env order (``RESONITE_IO_SOCKET`` → ``RESONITE_IO_SOCKET_DIR``
    → ``~/.resonite-io/``), so a socket that appears mid-wait is picked up
    and stale sockets whose owning engine PID is gone are skipped by the
    resolver. Connecting directly (bypassing the modality client) keeps the
    poll from firing the once-per-process version-mismatch probe each round.

    Args:
        socket_path: Explicit UDS path to wait on, or ``None`` to resolve it
            on each attempt.
        timeout: Maximum seconds to wait. ``None`` waits indefinitely;
            ``<= 0`` makes a single attempt.
        interval: Seconds to sleep between attempts (clamped so the wait
            never overshoots ``timeout``).

    Returns:
        The resolved UDS path that answered the ping.

    Raises:
        TimeoutError: ``timeout`` elapsed before a ping succeeded.
        AmbiguousSocketError: Multiple live sockets matched and no explicit
            path was given — waiting cannot disambiguate, so it propagates.
        grpclib.exceptions.GRPCError: The server answered with a
            non-retryable status. ``FAILED_PRECONDITION`` means "not ready
            yet" and is retried; every other status propagates.
    """
    deadline = None if timeout is None else time.monotonic() + timeout
    last_exc: Exception | None = None
    while True:
        try:
            path = socket_path or resolve_socket_path()
            channel = Channel(path=path)
            try:
                await ConnectionStub(channel).ping(PingRequest(message="wait"))
            finally:
                channel.close()
            return path
        except SocketNotFoundError as exc:
            last_exc = exc
        except OSError as exc:
            last_exc = exc
        except GRPCError as exc:
            if exc.status is not Status.FAILED_PRECONDITION:
                raise
            last_exc = exc

        if deadline is None:
            await asyncio.sleep(interval)
            continue
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            raise TimeoutError(
                f"Resonite IO socket did not become ready in "
                f"{timeout:.3f}s (last attempt: {last_exc})"
            ) from last_exc
        await asyncio.sleep(min(interval, remaining))

resoio.SocketNotFoundError

Bases: RuntimeError

No resonite-*.sock matched the configured search directory.

resoio.AmbiguousSocketError

Bases: RuntimeError

Multiple candidate sockets found; set RESONITE_IO_SOCKET to pick one.