Websocket Client
A pool of remote monty workers reached over a WebSocket instead of local subprocesses — the intended peer is
monty-server.
See running monty-server.
Async context manager owning a pool of remote monty workers reached over a
WebSocket. The intended peer is monty-server (the production server: one
monty subprocess child per connection, plus capacity/timeout policy and
graceful drain), but any server that accepts the connection and bridges to
a worker fits — a relay pairing it with a child that dialed in from the
other end, or the dev relay scripts/websocket_relay.py.
Like AsyncMonty, but instead of spawning local subprocesses each checkout
dials the configured URL. There is no sync counterpart — remote turns are
network-bound. checkout() yields the same AsyncMontySession.
A monty-server enforces its own policy on top of the pool’s. On SIGTERM
drain it answers the session’s next request with MontyShutdown, whose
dump restores the session onto another server; every other server-side
drop (idle, session or turn timeout, capacity) closes the connection and
raises MontyDisconnectError.
async with AsyncMontyWebsocket('ws://127.0.0.1:8799') as pool:
async with pool.checkout() as session:
result = await session.feed_run('1 + 1')
def __new__(
cls,
url: str,
*,
max_processes: int | None = None,
checkout_timeout: float | None = None,
request_timeout: float | None = 10.0,
) -> Self
Configure a remote worker pool; connections are made by async with and
each checkout (no workers are pre-warmed).
url : str
ws:///wss:// URL to dial — a relay, or any server that
bridges to a worker. Dialed verbatim; any session/rendezvous routing the URL
needs (e.g. a /<uuid>/parent path for a relay) must already be
in it.
Cap on concurrent connections (defaults to the CPU count); checkouts beyond it wait.
Seconds checkout() waits for capacity before
raising TimeoutError. None waits forever.
Hard per-turn deadline in seconds (default 10.0) — a
worker that exceeds it has its connection killed and the call
raises MontyCrashedError with timed_out=True. This also
bounds the wait when a relay accepts the connection but never
produces a worker. Pass None to wait indefinitely.
Note that install_dependencies is a turn too, so the default
10.0 is often too low for it — a real uv pip install can exceed
it. Raise request_timeout (or pass None) when installing
dependencies over the WebSocket transport.
def checkout(
*,
script_name: str = 'main.py',
limits: ResourceLimits | None = None,
type_check: bool = False,
type_check_stubs: str | None = None,
type_check_format: TypeCheckFormat | None = None,
type_check_color: bool = False,
assert_message_annotations: bool | int = ...,
) -> AsyncMontySession
Prepare a REPL session served by a dedicated remote connection.
Identical to AsyncMonty.checkout; the connection is opened by
async with on the returned session.