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.
@@ -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
+ ```
@@ -1,3 +1,3 @@
1
- __version__ = "0.0.14"
1
+ __version__ = "0.0.16"
2
2
  from .core import *
3
3
  from .ops import *
@@ -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): client_class,client_factory = ConKernelClient,Type(ConKernelClient)
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
+ ```
@@ -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
-
@@ -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
-