Concurrent-safe Jupyter KernelClient
Project description
conkernelclient
Background
Jupyter’s KernelClient is designed around a simple request-reply
pattern: you send one message on the shell channel, wait for its reply,
then send the next. This works fine for a single-threaded notebook, but
falls apart when you need concurrent execution. For instance, running
multiple cells in parallel, or letting an LLM tool loop fire off code
while a long-running computation is still in flight. The underlying ZMQ
socket isn’t safe to share across tasks, and there’s no built-in
mechanism to route replies back to the correct caller when multiple
requests are outstanding.
conkernelclient solves this with
ConKernelClient,
a drop-in replacement for AsyncKernelClient that makes concurrent
execute() calls safe. It patches Session.send to synchronise with
the ZMQ I/O thread (preventing a race where two sends interleave), and
spins up a dedicated reader task on the shell channel that demultiplexes
incoming replies by message ID. Each execute(..., reply=True) call
gets its own
asyncio.Queue,
so multiple coroutines can await their replies independently without
interfering with each other.
Installation
Install from pypi
$ pip install conkernelclient
How to use
from conkernelclient import *
The main entry point is
ConKernelManager,
a drop-in replacement for AsyncKernelManager that creates
ConKernelClient
instances. Start a kernel and connect a client in the usual way:
import asyncio
from jupyter_client.session import Session
km = ConKernelManager(session=Session(key=b'x'))
await km.start_kernel()
kc = await km.client().start_channels()
await kc.is_alive()
True
Once connected, execute() works like the standard client. Pass
reply=True to await the shell reply, or reply=False (the default) to
fire-and-forget and collect results later via get_pubs:
r = await kc.execute('2+1', timeout=1, reply=True)
r['content']['status']
'ok'
The key feature is safe concurrent execution. Multiple
execute(..., reply=True) calls can be outstanding simultaneously —
each gets its own
asyncio.Queue,
and a background reader task routes replies by message ID:
from fastcore.test import test_eq
a = kc.execute('x=2', reply=True)
b = kc.execute('y=3', reply=True)
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
test_eq(len(r), 2)
r[0]['parent_header']['msg_id']
'dab23f68-96c28dd9c776844176afdff1_66028_2'
Both replies arrive independently, each routed to the correct caller.
Without
ConKernelClient,
the second execute would either block waiting for the first to finish,
or the replies would get crossed.
As usual, we clean up when we’re done:
if await km.is_alive():
kc.stop_channels()
await km.shutdown_kernel()
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file conkernelclient-0.0.4.tar.gz.
File metadata
- Download URL: conkernelclient-0.0.4.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bae248611a4ec5d24fa6ea3a755f90e98c93b2b7c5c292f6f0ecf608e4ba4ff9
|
|
| MD5 |
67184d3bd7223b432cc3cc96bede1b4f
|
|
| BLAKE2b-256 |
593988a4f58b3d409dc28afe1efc5673d0c236142a18574b3231f4ede3a7c40b
|
File details
Details for the file conkernelclient-0.0.4-py3-none-any.whl.
File metadata
- Download URL: conkernelclient-0.0.4-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa16ff9593cd07bfa3d6197a92db3b8166fd2b6ae59bc3927d23b59f89fc67fd
|
|
| MD5 |
a90992928a2a8a6f166ca96a4492005f
|
|
| BLAKE2b-256 |
1bc965a7d45bbe93c327007434a2cf9cd3710516f835f48c592e4929ad776f6f
|