conkernelclient 0.0.14__tar.gz → 0.0.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conkernelclient-0.0.16/PKG-INFO +95 -0
- conkernelclient-0.0.16/README.md +75 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient/__init__.py +1 -1
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient/_modidx.py +2 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient/core.py +8 -2
- conkernelclient-0.0.16/conkernelclient.egg-info/PKG-INFO +95 -0
- conkernelclient-0.0.14/PKG-INFO +0 -128
- conkernelclient-0.0.14/README.md +0 -108
- conkernelclient-0.0.14/conkernelclient.egg-info/PKG-INFO +0 -128
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/LICENSE +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/MANIFEST.in +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient/ops.py +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/SOURCES.txt +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/dependency_links.txt +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/entry_points.txt +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/requires.txt +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/top_level.txt +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/pyproject.toml +0 -0
- {conkernelclient-0.0.14 → conkernelclient-0.0.16}/setup.cfg +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: conkernelclient
|
|
3
|
+
Version: 0.0.16
|
|
4
|
+
Summary: Concurrent-safe Jupyter KernelClient
|
|
5
|
+
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/conkernelclient
|
|
8
|
+
Project-URL: Documentation, https://AnswerDotAI.github.io/conkernelclient/
|
|
9
|
+
Keywords: nbdev
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: jupyter_client
|
|
16
|
+
Requires-Dist: fastcore
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: ipykernel; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# conkernelclient
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
+
|
|
26
|
+
## Background
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
*conkernelclient* solves this with [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#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`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), so multiple coroutines can `await` their replies independently without interfering with each other.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
35
|
+
|
|
36
|
+
``` sh
|
|
37
|
+
$ pip install conkernelclient
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## How to use
|
|
41
|
+
|
|
42
|
+
``` python
|
|
43
|
+
from conkernelclient import *
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The main entry point is [`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager), a drop-in replacement for `AsyncKernelManager` that creates [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient) instances. Start a kernel and connect a client in the usual way:
|
|
47
|
+
|
|
48
|
+
``` python
|
|
49
|
+
import asyncio
|
|
50
|
+
from jupyter_client.session import Session
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
``` python
|
|
54
|
+
km = ConKernelManager(session=Session(key=b'x'))
|
|
55
|
+
await km.start_kernel()
|
|
56
|
+
kc = await km.client().start_channels()
|
|
57
|
+
await kc.is_alive()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
True
|
|
61
|
+
|
|
62
|
+
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`:
|
|
63
|
+
|
|
64
|
+
``` python
|
|
65
|
+
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
66
|
+
r['content']['status']
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
'ok'
|
|
70
|
+
|
|
71
|
+
The key feature is safe concurrent execution. Multiple `execute(..., reply=True)` calls can be outstanding simultaneously — each gets its own [`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), and a background reader task routes replies by message ID:
|
|
72
|
+
|
|
73
|
+
``` python
|
|
74
|
+
from fastcore.test import test_eq
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
``` python
|
|
78
|
+
a = kc.execute('x=2', reply=True)
|
|
79
|
+
b = kc.execute('y=3', reply=True)
|
|
80
|
+
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
81
|
+
test_eq(len(r), 2)
|
|
82
|
+
r[0]['parent_header']['msg_id']
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
'8c84fe1c-20242a1940eecfc5a1a31500_61486_3'
|
|
86
|
+
|
|
87
|
+
Both replies arrive independently, each routed to the correct caller. Without [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient), the second `execute` would either block waiting for the first to finish, or the replies would get crossed.
|
|
88
|
+
|
|
89
|
+
As usual, we clean up when we’re done:
|
|
90
|
+
|
|
91
|
+
``` python
|
|
92
|
+
if await km.is_alive():
|
|
93
|
+
kc.stop_channels()
|
|
94
|
+
await km.shutdown_kernel()
|
|
95
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# conkernelclient
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
|
+
|
|
6
|
+
## Background
|
|
7
|
+
|
|
8
|
+
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.
|
|
9
|
+
|
|
10
|
+
*conkernelclient* solves this with [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#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`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), so multiple coroutines can `await` their replies independently without interfering with each other.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
15
|
+
|
|
16
|
+
``` sh
|
|
17
|
+
$ pip install conkernelclient
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to use
|
|
21
|
+
|
|
22
|
+
``` python
|
|
23
|
+
from conkernelclient import *
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The main entry point is [`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager), a drop-in replacement for `AsyncKernelManager` that creates [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient) instances. Start a kernel and connect a client in the usual way:
|
|
27
|
+
|
|
28
|
+
``` python
|
|
29
|
+
import asyncio
|
|
30
|
+
from jupyter_client.session import Session
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
``` python
|
|
34
|
+
km = ConKernelManager(session=Session(key=b'x'))
|
|
35
|
+
await km.start_kernel()
|
|
36
|
+
kc = await km.client().start_channels()
|
|
37
|
+
await kc.is_alive()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
True
|
|
41
|
+
|
|
42
|
+
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`:
|
|
43
|
+
|
|
44
|
+
``` python
|
|
45
|
+
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
46
|
+
r['content']['status']
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
'ok'
|
|
50
|
+
|
|
51
|
+
The key feature is safe concurrent execution. Multiple `execute(..., reply=True)` calls can be outstanding simultaneously — each gets its own [`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), and a background reader task routes replies by message ID:
|
|
52
|
+
|
|
53
|
+
``` python
|
|
54
|
+
from fastcore.test import test_eq
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
``` python
|
|
58
|
+
a = kc.execute('x=2', reply=True)
|
|
59
|
+
b = kc.execute('y=3', reply=True)
|
|
60
|
+
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
61
|
+
test_eq(len(r), 2)
|
|
62
|
+
r[0]['parent_header']['msg_id']
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
'8c84fe1c-20242a1940eecfc5a1a31500_61486_3'
|
|
66
|
+
|
|
67
|
+
Both replies arrive independently, each routed to the correct caller. Without [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient), the second `execute` would either block waiting for the first to finish, or the replies would get crossed.
|
|
68
|
+
|
|
69
|
+
As usual, we clean up when we’re done:
|
|
70
|
+
|
|
71
|
+
``` python
|
|
72
|
+
if await km.is_alive():
|
|
73
|
+
kc.stop_channels()
|
|
74
|
+
await km.shutdown_kernel()
|
|
75
|
+
```
|
|
@@ -23,6 +23,8 @@ d = { 'settings': { 'branch': 'main',
|
|
|
23
23
|
'conkernelclient.core.ConKernelClient.stop_channels': ( 'core.html#conkernelclient.stop_channels',
|
|
24
24
|
'conkernelclient/core.py'),
|
|
25
25
|
'conkernelclient.core.ConKernelManager': ('core.html#conkernelmanager', 'conkernelclient/core.py'),
|
|
26
|
+
'conkernelclient.core.ConKernelManager._transport_encryption_default': ( 'core.html#conkernelmanager._transport_encryption_default',
|
|
27
|
+
'conkernelclient/core.py'),
|
|
26
28
|
'conkernelclient.core.DeadKernelError': ('core.html#deadkernelerror', 'conkernelclient/core.py'),
|
|
27
29
|
'conkernelclient.core._send': ('core.html#_send', 'conkernelclient/core.py'),
|
|
28
30
|
'conkernelclient.core.apply_session_patch': ( 'core.html#apply_session_patch',
|
|
@@ -11,7 +11,7 @@ __all__ = ['DeadKernelError', 'apply_session_patch', 'ConKernelClient', 'ConKern
|
|
|
11
11
|
from jupyter_client import AsyncKernelClient, AsyncKernelManager
|
|
12
12
|
from jupyter_client.session import Session
|
|
13
13
|
from zmq.error import ZMQError
|
|
14
|
-
from traitlets import Type
|
|
14
|
+
from traitlets import Type, default
|
|
15
15
|
import asyncio, zmq.asyncio, time, logging
|
|
16
16
|
|
|
17
17
|
# %% ../nbs/00_core.ipynb #737a0fc1
|
|
@@ -48,6 +48,9 @@ def apply_session_patch():
|
|
|
48
48
|
class ConKernelClient(AsyncKernelClient):
|
|
49
49
|
def __init__(self, *args, **kwargs):
|
|
50
50
|
apply_session_patch()
|
|
51
|
+
# jupyter_client's `get_connection_info` returns curve keys as str, but the client traits are `Bytes`
|
|
52
|
+
for k in ('curve_publickey','curve_secretkey'):
|
|
53
|
+
if isinstance(kwargs.get(k), str): kwargs[k] = kwargs[k].encode()
|
|
51
54
|
super().__init__(*args, **kwargs)
|
|
52
55
|
|
|
53
56
|
def _fail_pending(self, exc:Exception, skip=None):
|
|
@@ -134,4 +137,7 @@ class ConKernelClient(AsyncKernelClient):
|
|
|
134
137
|
await asyncio.sleep(0.01)
|
|
135
138
|
|
|
136
139
|
# %% ../nbs/00_core.ipynb #b828c222
|
|
137
|
-
class ConKernelManager(AsyncKernelManager):
|
|
140
|
+
class ConKernelManager(AsyncKernelManager):
|
|
141
|
+
client_class,client_factory = ConKernelClient,Type(ConKernelClient)
|
|
142
|
+
@default('transport_encryption')
|
|
143
|
+
def _transport_encryption_default(self): return 'auto'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: conkernelclient
|
|
3
|
+
Version: 0.0.16
|
|
4
|
+
Summary: Concurrent-safe Jupyter KernelClient
|
|
5
|
+
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/conkernelclient
|
|
8
|
+
Project-URL: Documentation, https://AnswerDotAI.github.io/conkernelclient/
|
|
9
|
+
Keywords: nbdev
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: jupyter_client
|
|
16
|
+
Requires-Dist: fastcore
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: ipykernel; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# conkernelclient
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
+
|
|
26
|
+
## Background
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
*conkernelclient* solves this with [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#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`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), so multiple coroutines can `await` their replies independently without interfering with each other.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
35
|
+
|
|
36
|
+
``` sh
|
|
37
|
+
$ pip install conkernelclient
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## How to use
|
|
41
|
+
|
|
42
|
+
``` python
|
|
43
|
+
from conkernelclient import *
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The main entry point is [`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager), a drop-in replacement for `AsyncKernelManager` that creates [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient) instances. Start a kernel and connect a client in the usual way:
|
|
47
|
+
|
|
48
|
+
``` python
|
|
49
|
+
import asyncio
|
|
50
|
+
from jupyter_client.session import Session
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
``` python
|
|
54
|
+
km = ConKernelManager(session=Session(key=b'x'))
|
|
55
|
+
await km.start_kernel()
|
|
56
|
+
kc = await km.client().start_channels()
|
|
57
|
+
await kc.is_alive()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
True
|
|
61
|
+
|
|
62
|
+
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`:
|
|
63
|
+
|
|
64
|
+
``` python
|
|
65
|
+
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
66
|
+
r['content']['status']
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
'ok'
|
|
70
|
+
|
|
71
|
+
The key feature is safe concurrent execution. Multiple `execute(..., reply=True)` calls can be outstanding simultaneously — each gets its own [`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue), and a background reader task routes replies by message ID:
|
|
72
|
+
|
|
73
|
+
``` python
|
|
74
|
+
from fastcore.test import test_eq
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
``` python
|
|
78
|
+
a = kc.execute('x=2', reply=True)
|
|
79
|
+
b = kc.execute('y=3', reply=True)
|
|
80
|
+
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
81
|
+
test_eq(len(r), 2)
|
|
82
|
+
r[0]['parent_header']['msg_id']
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
'8c84fe1c-20242a1940eecfc5a1a31500_61486_3'
|
|
86
|
+
|
|
87
|
+
Both replies arrive independently, each routed to the correct caller. Without [`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient), the second `execute` would either block waiting for the first to finish, or the replies would get crossed.
|
|
88
|
+
|
|
89
|
+
As usual, we clean up when we’re done:
|
|
90
|
+
|
|
91
|
+
``` python
|
|
92
|
+
if await km.is_alive():
|
|
93
|
+
kc.stop_channels()
|
|
94
|
+
await km.shutdown_kernel()
|
|
95
|
+
```
|
conkernelclient-0.0.14/PKG-INFO
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: conkernelclient
|
|
3
|
-
Version: 0.0.14
|
|
4
|
-
Summary: Concurrent-safe Jupyter KernelClient
|
|
5
|
-
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Repository, https://github.com/AnswerDotAI/conkernelclient
|
|
8
|
-
Project-URL: Documentation, https://AnswerDotAI.github.io/conkernelclient/
|
|
9
|
-
Keywords: nbdev
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: jupyter_client
|
|
16
|
-
Requires-Dist: fastcore
|
|
17
|
-
Provides-Extra: dev
|
|
18
|
-
Requires-Dist: ipykernel; extra == "dev"
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# conkernelclient
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
-
|
|
26
|
-
## Background
|
|
27
|
-
|
|
28
|
-
Jupyter’s `KernelClient` is designed around a simple request-reply
|
|
29
|
-
pattern: you send one message on the shell channel, wait for its reply,
|
|
30
|
-
then send the next. This works fine for a single-threaded notebook, but
|
|
31
|
-
falls apart when you need concurrent execution. For instance, running
|
|
32
|
-
multiple cells in parallel, or letting an LLM tool loop fire off code
|
|
33
|
-
while a long-running computation is still in flight. The underlying ZMQ
|
|
34
|
-
socket isn’t safe to share across tasks, and there’s no built-in
|
|
35
|
-
mechanism to route replies back to the correct caller when multiple
|
|
36
|
-
requests are outstanding.
|
|
37
|
-
|
|
38
|
-
*conkernelclient* solves this with
|
|
39
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
40
|
-
a drop-in replacement for `AsyncKernelClient` that makes concurrent
|
|
41
|
-
`execute()` calls safe. It patches `Session.send` to synchronise with
|
|
42
|
-
the ZMQ I/O thread (preventing a race where two sends interleave), and
|
|
43
|
-
spins up a dedicated reader task on the shell channel that demultiplexes
|
|
44
|
-
incoming replies by message ID. Each `execute(..., reply=True)` call
|
|
45
|
-
gets its own
|
|
46
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
47
|
-
so multiple coroutines can `await` their replies independently without
|
|
48
|
-
interfering with each other.
|
|
49
|
-
|
|
50
|
-
## Installation
|
|
51
|
-
|
|
52
|
-
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
53
|
-
|
|
54
|
-
``` sh
|
|
55
|
-
$ pip install conkernelclient
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## How to use
|
|
59
|
-
|
|
60
|
-
``` python
|
|
61
|
-
from conkernelclient import *
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The main entry point is
|
|
65
|
-
[`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager),
|
|
66
|
-
a drop-in replacement for `AsyncKernelManager` that creates
|
|
67
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient)
|
|
68
|
-
instances. Start a kernel and connect a client in the usual way:
|
|
69
|
-
|
|
70
|
-
``` python
|
|
71
|
-
import asyncio
|
|
72
|
-
from jupyter_client.session import Session
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
``` python
|
|
76
|
-
km = ConKernelManager(session=Session(key=b'x'))
|
|
77
|
-
await km.start_kernel()
|
|
78
|
-
kc = await km.client().start_channels()
|
|
79
|
-
await kc.is_alive()
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
True
|
|
83
|
-
|
|
84
|
-
Once connected, `execute()` works like the standard client. Pass
|
|
85
|
-
`reply=True` to await the shell reply, or `reply=False` (the default) to
|
|
86
|
-
fire-and-forget and collect results later via `get_pubs`:
|
|
87
|
-
|
|
88
|
-
``` python
|
|
89
|
-
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
90
|
-
r['content']['status']
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
'ok'
|
|
94
|
-
|
|
95
|
-
The key feature is safe concurrent execution. Multiple
|
|
96
|
-
`execute(..., reply=True)` calls can be outstanding simultaneously —
|
|
97
|
-
each gets its own
|
|
98
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
99
|
-
and a background reader task routes replies by message ID:
|
|
100
|
-
|
|
101
|
-
``` python
|
|
102
|
-
from fastcore.test import test_eq
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
``` python
|
|
106
|
-
a = kc.execute('x=2', reply=True)
|
|
107
|
-
b = kc.execute('y=3', reply=True)
|
|
108
|
-
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
109
|
-
test_eq(len(r), 2)
|
|
110
|
-
r[0]['parent_header']['msg_id']
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
'dab23f68-96c28dd9c776844176afdff1_66028_2'
|
|
114
|
-
|
|
115
|
-
Both replies arrive independently, each routed to the correct caller.
|
|
116
|
-
Without
|
|
117
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
118
|
-
the second `execute` would either block waiting for the first to finish,
|
|
119
|
-
or the replies would get crossed.
|
|
120
|
-
|
|
121
|
-
As usual, we clean up when we’re done:
|
|
122
|
-
|
|
123
|
-
``` python
|
|
124
|
-
if await km.is_alive():
|
|
125
|
-
kc.stop_channels()
|
|
126
|
-
await km.shutdown_kernel()
|
|
127
|
-
```
|
|
128
|
-
|
conkernelclient-0.0.14/README.md
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# conkernelclient
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
|
-
|
|
6
|
-
## Background
|
|
7
|
-
|
|
8
|
-
Jupyter’s `KernelClient` is designed around a simple request-reply
|
|
9
|
-
pattern: you send one message on the shell channel, wait for its reply,
|
|
10
|
-
then send the next. This works fine for a single-threaded notebook, but
|
|
11
|
-
falls apart when you need concurrent execution. For instance, running
|
|
12
|
-
multiple cells in parallel, or letting an LLM tool loop fire off code
|
|
13
|
-
while a long-running computation is still in flight. The underlying ZMQ
|
|
14
|
-
socket isn’t safe to share across tasks, and there’s no built-in
|
|
15
|
-
mechanism to route replies back to the correct caller when multiple
|
|
16
|
-
requests are outstanding.
|
|
17
|
-
|
|
18
|
-
*conkernelclient* solves this with
|
|
19
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
20
|
-
a drop-in replacement for `AsyncKernelClient` that makes concurrent
|
|
21
|
-
`execute()` calls safe. It patches `Session.send` to synchronise with
|
|
22
|
-
the ZMQ I/O thread (preventing a race where two sends interleave), and
|
|
23
|
-
spins up a dedicated reader task on the shell channel that demultiplexes
|
|
24
|
-
incoming replies by message ID. Each `execute(..., reply=True)` call
|
|
25
|
-
gets its own
|
|
26
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
27
|
-
so multiple coroutines can `await` their replies independently without
|
|
28
|
-
interfering with each other.
|
|
29
|
-
|
|
30
|
-
## Installation
|
|
31
|
-
|
|
32
|
-
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
33
|
-
|
|
34
|
-
``` sh
|
|
35
|
-
$ pip install conkernelclient
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## How to use
|
|
39
|
-
|
|
40
|
-
``` python
|
|
41
|
-
from conkernelclient import *
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
The main entry point is
|
|
45
|
-
[`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager),
|
|
46
|
-
a drop-in replacement for `AsyncKernelManager` that creates
|
|
47
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient)
|
|
48
|
-
instances. Start a kernel and connect a client in the usual way:
|
|
49
|
-
|
|
50
|
-
``` python
|
|
51
|
-
import asyncio
|
|
52
|
-
from jupyter_client.session import Session
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
``` python
|
|
56
|
-
km = ConKernelManager(session=Session(key=b'x'))
|
|
57
|
-
await km.start_kernel()
|
|
58
|
-
kc = await km.client().start_channels()
|
|
59
|
-
await kc.is_alive()
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
True
|
|
63
|
-
|
|
64
|
-
Once connected, `execute()` works like the standard client. Pass
|
|
65
|
-
`reply=True` to await the shell reply, or `reply=False` (the default) to
|
|
66
|
-
fire-and-forget and collect results later via `get_pubs`:
|
|
67
|
-
|
|
68
|
-
``` python
|
|
69
|
-
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
70
|
-
r['content']['status']
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
'ok'
|
|
74
|
-
|
|
75
|
-
The key feature is safe concurrent execution. Multiple
|
|
76
|
-
`execute(..., reply=True)` calls can be outstanding simultaneously —
|
|
77
|
-
each gets its own
|
|
78
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
79
|
-
and a background reader task routes replies by message ID:
|
|
80
|
-
|
|
81
|
-
``` python
|
|
82
|
-
from fastcore.test import test_eq
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
``` python
|
|
86
|
-
a = kc.execute('x=2', reply=True)
|
|
87
|
-
b = kc.execute('y=3', reply=True)
|
|
88
|
-
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
89
|
-
test_eq(len(r), 2)
|
|
90
|
-
r[0]['parent_header']['msg_id']
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
'dab23f68-96c28dd9c776844176afdff1_66028_2'
|
|
94
|
-
|
|
95
|
-
Both replies arrive independently, each routed to the correct caller.
|
|
96
|
-
Without
|
|
97
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
98
|
-
the second `execute` would either block waiting for the first to finish,
|
|
99
|
-
or the replies would get crossed.
|
|
100
|
-
|
|
101
|
-
As usual, we clean up when we’re done:
|
|
102
|
-
|
|
103
|
-
``` python
|
|
104
|
-
if await km.is_alive():
|
|
105
|
-
kc.stop_channels()
|
|
106
|
-
await km.shutdown_kernel()
|
|
107
|
-
```
|
|
108
|
-
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: conkernelclient
|
|
3
|
-
Version: 0.0.14
|
|
4
|
-
Summary: Concurrent-safe Jupyter KernelClient
|
|
5
|
-
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Project-URL: Repository, https://github.com/AnswerDotAI/conkernelclient
|
|
8
|
-
Project-URL: Documentation, https://AnswerDotAI.github.io/conkernelclient/
|
|
9
|
-
Keywords: nbdev
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
-
Requires-Python: >=3.10
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: jupyter_client
|
|
16
|
-
Requires-Dist: fastcore
|
|
17
|
-
Provides-Extra: dev
|
|
18
|
-
Requires-Dist: ipykernel; extra == "dev"
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# conkernelclient
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
25
|
-
|
|
26
|
-
## Background
|
|
27
|
-
|
|
28
|
-
Jupyter’s `KernelClient` is designed around a simple request-reply
|
|
29
|
-
pattern: you send one message on the shell channel, wait for its reply,
|
|
30
|
-
then send the next. This works fine for a single-threaded notebook, but
|
|
31
|
-
falls apart when you need concurrent execution. For instance, running
|
|
32
|
-
multiple cells in parallel, or letting an LLM tool loop fire off code
|
|
33
|
-
while a long-running computation is still in flight. The underlying ZMQ
|
|
34
|
-
socket isn’t safe to share across tasks, and there’s no built-in
|
|
35
|
-
mechanism to route replies back to the correct caller when multiple
|
|
36
|
-
requests are outstanding.
|
|
37
|
-
|
|
38
|
-
*conkernelclient* solves this with
|
|
39
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
40
|
-
a drop-in replacement for `AsyncKernelClient` that makes concurrent
|
|
41
|
-
`execute()` calls safe. It patches `Session.send` to synchronise with
|
|
42
|
-
the ZMQ I/O thread (preventing a race where two sends interleave), and
|
|
43
|
-
spins up a dedicated reader task on the shell channel that demultiplexes
|
|
44
|
-
incoming replies by message ID. Each `execute(..., reply=True)` call
|
|
45
|
-
gets its own
|
|
46
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
47
|
-
so multiple coroutines can `await` their replies independently without
|
|
48
|
-
interfering with each other.
|
|
49
|
-
|
|
50
|
-
## Installation
|
|
51
|
-
|
|
52
|
-
Install from [pypi](https://pypi.org/project/conkernelclient/)
|
|
53
|
-
|
|
54
|
-
``` sh
|
|
55
|
-
$ pip install conkernelclient
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## How to use
|
|
59
|
-
|
|
60
|
-
``` python
|
|
61
|
-
from conkernelclient import *
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The main entry point is
|
|
65
|
-
[`ConKernelManager`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelmanager),
|
|
66
|
-
a drop-in replacement for `AsyncKernelManager` that creates
|
|
67
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient)
|
|
68
|
-
instances. Start a kernel and connect a client in the usual way:
|
|
69
|
-
|
|
70
|
-
``` python
|
|
71
|
-
import asyncio
|
|
72
|
-
from jupyter_client.session import Session
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
``` python
|
|
76
|
-
km = ConKernelManager(session=Session(key=b'x'))
|
|
77
|
-
await km.start_kernel()
|
|
78
|
-
kc = await km.client().start_channels()
|
|
79
|
-
await kc.is_alive()
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
True
|
|
83
|
-
|
|
84
|
-
Once connected, `execute()` works like the standard client. Pass
|
|
85
|
-
`reply=True` to await the shell reply, or `reply=False` (the default) to
|
|
86
|
-
fire-and-forget and collect results later via `get_pubs`:
|
|
87
|
-
|
|
88
|
-
``` python
|
|
89
|
-
r = await kc.execute('2+1', timeout=1, reply=True)
|
|
90
|
-
r['content']['status']
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
'ok'
|
|
94
|
-
|
|
95
|
-
The key feature is safe concurrent execution. Multiple
|
|
96
|
-
`execute(..., reply=True)` calls can be outstanding simultaneously —
|
|
97
|
-
each gets its own
|
|
98
|
-
[`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue),
|
|
99
|
-
and a background reader task routes replies by message ID:
|
|
100
|
-
|
|
101
|
-
``` python
|
|
102
|
-
from fastcore.test import test_eq
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
``` python
|
|
106
|
-
a = kc.execute('x=2', reply=True)
|
|
107
|
-
b = kc.execute('y=3', reply=True)
|
|
108
|
-
r = await asyncio.wait_for(asyncio.gather(a, b), timeout=2)
|
|
109
|
-
test_eq(len(r), 2)
|
|
110
|
-
r[0]['parent_header']['msg_id']
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
'dab23f68-96c28dd9c776844176afdff1_66028_2'
|
|
114
|
-
|
|
115
|
-
Both replies arrive independently, each routed to the correct caller.
|
|
116
|
-
Without
|
|
117
|
-
[`ConKernelClient`](https://AnswerDotAI.github.io/conkernelclient/core.html#conkernelclient),
|
|
118
|
-
the second `execute` would either block waiting for the first to finish,
|
|
119
|
-
or the replies would get crossed.
|
|
120
|
-
|
|
121
|
-
As usual, we clean up when we’re done:
|
|
122
|
-
|
|
123
|
-
``` python
|
|
124
|
-
if await km.is_alive():
|
|
125
|
-
kc.stop_channels()
|
|
126
|
-
await km.shutdown_kernel()
|
|
127
|
-
```
|
|
128
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{conkernelclient-0.0.14 → conkernelclient-0.0.16}/conkernelclient.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|