Metadata-Version: 2.4
Name: jupyasyncclient
Version: 0.2.1
Summary: Async kernel client for Jupyter Server via HTTP/WebSocket
Author: Jeremy Howard
License: Apache-2
Project-URL: Repository, https://github.com/AnswerDotAI/jupyasyncclient
Project-URL: Documentation, https://AnswerDotAI.github.io/jupyasyncclient/
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: jupywire>=0.0.1
Requires-Dist: httpx>=0.27
Requires-Dist: fastcore>=2.1.17
Requires-Dist: websockets>=13
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: jupyter_server>=2; extra == "dev"
Requires-Dist: ipykernel; extra == "dev"
Requires-Dist: pytest-xdist; extra == "dev"
Requires-Dist: pytest-timeout; extra == "dev"
Requires-Dist: jupygate>=0.0.1; extra == "dev"
Requires-Dist: ipymini>=0.1.17; extra == "dev"
Requires-Dist: ipyfuncs>=0.0.1; extra == "dev"

# jupyasyncclient


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

jupyasyncclient runs code on Jupyter kernels hosted by any server speaking the standard kernels API: [jupygate](https://github.com/AnswerDotAI/jupygate), or jupyter_server. Kernel lifecycle (create, interrupt, restart, delete) is plain HTTP; messaging is one websocket per client carrying standard Jupyter message dicts with a `channel` key. There is no zmq and no tornado in the client process, and every send is genuinely awaited - the zmq-side subtleties (sync-send edge consumption, slow-joiner subscriptions, socket identity contracts) all live server-side.

Three classes cover the usual shapes, mirroring jupyter_client where familiarity helps:

- `JupyAsyncKernelClient` - one kernel: lifecycle, channels, and messaging. `execute`, `complete`, `inspect`, `history`, `kernel_info`, `wait_for_ready`, and per-channel `get_*_msg` accessors work like their jupyter_client namesakes; any request can await its reply directly with `reply=True`; and every `*_request` message type in the protocol is callable by name, subshell requests included, so new protocol messages need no client release.
- `JupyAsyncKernelManager` - start/stop one kernel and mint clients for it.
- `JupyAsyncMultiKernelManager` - a fleet, with keyed reuse: `ensure_kernel('some-key')` returns the live kernel registered under that key or starts a fresh one.

The [`core`](00_core.ipynb) notebook builds the client bottom-up with every method demonstrated against a live server; the managers are thin HTTP wrappers and live in plain modules.

## Install

``` sh
pip install jupyasyncclient
```

Plus a server to talk to. The examples here use jupygate serving [ipymini](https://github.com/AnswerDotAI/ipymini) kernels; a stock jupyter_server works identically (the test suite runs against one).

## Use

``` python
import asyncio, time
from jupygate.core import create_app, serve
```

``` python
server = serve(create_app(), port=8812, in_thread=True)
while not getattr(server, 'started', False): time.sleep(0.05)
server.started
```

    True

`start_new_server_kernel` gives a running kernel and a ready client in one call:

``` python
km, kc = await start_new_server_kernel('http://127.0.0.1:8812')
rep = await kc.execute("print('hello'); 6*7", reply=True, timeout=30)
rep['content']['status']
```

    'ok'

Outputs arrive on the iopub queue, like jupyter_client:

``` python
m = await kc.get_iopub_msg(timeout=15)
while m['msg_type'] != 'stream': m = await kc.get_iopub_msg(timeout=15)
m['content']['text']
```

    'hello\n'

`input()` in the kernel becomes an `input_request` on the stdin queue; answer it with `input`, which parents the reply properly:

``` python
fut = asyncio.ensure_future(kc.execute("name = input('who? ')", reply=True, timeout=30))
prompt = await kc.get_stdin_msg(timeout=15)
kc.input('Jeremy')
(await fut)['content']['status']
```

    'ok'

Any protocol request type works by name, `reply=True` awaiting its reply - here JEP 91 subshells, no client support required beyond the message type:

``` python
sub = (await kc.create_subshell(reply=True, timeout=15))['content']['subshell_id']
rep = await kc.execute('40+2', reply=True, timeout=30, subshell_id=sub)
await kc.delete_subshell(sub, reply=True, timeout=15)
rep['content']['status']
```

    'ok'

The multimanager runs fleets, with keyed reuse for “the kernel for X” patterns:

``` python
mkm = JupyAsyncMultiKernelManager('http://127.0.0.1:8812')
k1 = await mkm.ensure_kernel('analysis')
k2 = await mkm.ensure_kernel('analysis')
k1 == k2
```

    True

``` python
await kc.aclose()
await km.aclose()
await mkm.shutdown_all()
await mkm.aclose()
```

Auth is a bearer token when the server requires one: pass `token=...` to any of the three classes and it is sent as an `Authorization` header on HTTP and a query param on the websocket.
