jupyasyncclient 0.2.0__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,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: jupyasyncclient
3
+ Version: 0.2.0
4
+ Summary: Async kernel client for Jupyter Server via HTTP/WebSocket
5
+ Author: Jeremy Howard
6
+ License: Apache-2
7
+ Project-URL: Repository, https://github.com/AnswerDotAI/jupyasyncclient
8
+ Project-URL: Documentation, https://AnswerDotAI.github.io/jupyasyncclient/
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: jupywire>=0.0.1
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: fastcore>=2.1.17
14
+ Requires-Dist: websockets>=13
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=7; extra == "dev"
17
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
18
+ Requires-Dist: jupyter_server>=2; extra == "dev"
19
+ Requires-Dist: ipykernel; extra == "dev"
20
+ Requires-Dist: pytest-xdist; extra == "dev"
21
+ Requires-Dist: pytest-timeout; extra == "dev"
22
+ Requires-Dist: jupygate>=0.0.1; extra == "dev"
23
+ Requires-Dist: ipymini>=0.1.17; extra == "dev"
24
+ Requires-Dist: ipyfuncs>=0.0.1; extra == "dev"
25
+
26
+ # jupyasyncclient
27
+
28
+
29
+ <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
30
+
31
+ 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.
32
+
33
+ Three classes cover the usual shapes, mirroring jupyter_client where familiarity helps:
34
+
35
+ - `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.
36
+ - `JupyAsyncKernelManager` - start/stop one kernel and mint clients for it.
37
+ - `JupyAsyncMultiKernelManager` - a fleet, with keyed reuse: `ensure_kernel('some-key')` returns the live kernel registered under that key or starts a fresh one.
38
+
39
+ 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.
40
+
41
+ ## Install
42
+
43
+ ``` sh
44
+ pip install jupyasyncclient
45
+ ```
46
+
47
+ 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).
48
+
49
+ ## Use
50
+
51
+ ``` python
52
+ import asyncio, time
53
+ from jupygate.core import create_app, serve
54
+ ```
55
+
56
+ ``` python
57
+ server = serve(create_app(), port=8812, in_thread=True)
58
+ while not getattr(server, 'started', False): time.sleep(0.05)
59
+ server.started
60
+ ```
61
+
62
+ True
63
+
64
+ `start_new_server_kernel` gives a running kernel and a ready client in one call:
65
+
66
+ ``` python
67
+ km, kc = await start_new_server_kernel('http://127.0.0.1:8812')
68
+ rep = await kc.execute("print('hello'); 6*7", reply=True, timeout=30)
69
+ rep['content']['status']
70
+ ```
71
+
72
+ 'ok'
73
+
74
+ Outputs arrive on the iopub queue, like jupyter_client:
75
+
76
+ ``` python
77
+ m = await kc.get_iopub_msg(timeout=15)
78
+ while m['msg_type'] != 'stream': m = await kc.get_iopub_msg(timeout=15)
79
+ m['content']['text']
80
+ ```
81
+
82
+ 'hello\n'
83
+
84
+ `input()` in the kernel becomes an `input_request` on the stdin queue; answer it with `input`, which parents the reply properly:
85
+
86
+ ``` python
87
+ fut = asyncio.ensure_future(kc.execute("name = input('who? ')", reply=True, timeout=30))
88
+ prompt = await kc.get_stdin_msg(timeout=15)
89
+ kc.input('Jeremy')
90
+ (await fut)['content']['status']
91
+ ```
92
+
93
+ 'ok'
94
+
95
+ Any protocol request type works by name, `reply=True` awaiting its reply - here JEP 91 subshells, no client support required beyond the message type:
96
+
97
+ ``` python
98
+ sub = (await kc.create_subshell(reply=True, timeout=15))['content']['subshell_id']
99
+ rep = await kc.execute('40+2', reply=True, timeout=30, subshell_id=sub)
100
+ await kc.delete_subshell(sub, reply=True, timeout=15)
101
+ rep['content']['status']
102
+ ```
103
+
104
+ 'ok'
105
+
106
+ The multimanager runs fleets, with keyed reuse for “the kernel for X” patterns:
107
+
108
+ ``` python
109
+ mkm = JupyAsyncMultiKernelManager('http://127.0.0.1:8812')
110
+ k1 = await mkm.ensure_kernel('analysis')
111
+ k2 = await mkm.ensure_kernel('analysis')
112
+ k1 == k2
113
+ ```
114
+
115
+ True
116
+
117
+ ``` python
118
+ await kc.aclose()
119
+ await km.aclose()
120
+ await mkm.shutdown_all()
121
+ await mkm.aclose()
122
+ ```
123
+
124
+ 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.
@@ -0,0 +1,99 @@
1
+ # jupyasyncclient
2
+
3
+
4
+ <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5
+
6
+ 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.
7
+
8
+ Three classes cover the usual shapes, mirroring jupyter_client where familiarity helps:
9
+
10
+ - `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.
11
+ - `JupyAsyncKernelManager` - start/stop one kernel and mint clients for it.
12
+ - `JupyAsyncMultiKernelManager` - a fleet, with keyed reuse: `ensure_kernel('some-key')` returns the live kernel registered under that key or starts a fresh one.
13
+
14
+ 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.
15
+
16
+ ## Install
17
+
18
+ ``` sh
19
+ pip install jupyasyncclient
20
+ ```
21
+
22
+ 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).
23
+
24
+ ## Use
25
+
26
+ ``` python
27
+ import asyncio, time
28
+ from jupygate.core import create_app, serve
29
+ ```
30
+
31
+ ``` python
32
+ server = serve(create_app(), port=8812, in_thread=True)
33
+ while not getattr(server, 'started', False): time.sleep(0.05)
34
+ server.started
35
+ ```
36
+
37
+ True
38
+
39
+ `start_new_server_kernel` gives a running kernel and a ready client in one call:
40
+
41
+ ``` python
42
+ km, kc = await start_new_server_kernel('http://127.0.0.1:8812')
43
+ rep = await kc.execute("print('hello'); 6*7", reply=True, timeout=30)
44
+ rep['content']['status']
45
+ ```
46
+
47
+ 'ok'
48
+
49
+ Outputs arrive on the iopub queue, like jupyter_client:
50
+
51
+ ``` python
52
+ m = await kc.get_iopub_msg(timeout=15)
53
+ while m['msg_type'] != 'stream': m = await kc.get_iopub_msg(timeout=15)
54
+ m['content']['text']
55
+ ```
56
+
57
+ 'hello\n'
58
+
59
+ `input()` in the kernel becomes an `input_request` on the stdin queue; answer it with `input`, which parents the reply properly:
60
+
61
+ ``` python
62
+ fut = asyncio.ensure_future(kc.execute("name = input('who? ')", reply=True, timeout=30))
63
+ prompt = await kc.get_stdin_msg(timeout=15)
64
+ kc.input('Jeremy')
65
+ (await fut)['content']['status']
66
+ ```
67
+
68
+ 'ok'
69
+
70
+ Any protocol request type works by name, `reply=True` awaiting its reply - here JEP 91 subshells, no client support required beyond the message type:
71
+
72
+ ``` python
73
+ sub = (await kc.create_subshell(reply=True, timeout=15))['content']['subshell_id']
74
+ rep = await kc.execute('40+2', reply=True, timeout=30, subshell_id=sub)
75
+ await kc.delete_subshell(sub, reply=True, timeout=15)
76
+ rep['content']['status']
77
+ ```
78
+
79
+ 'ok'
80
+
81
+ The multimanager runs fleets, with keyed reuse for “the kernel for X” patterns:
82
+
83
+ ``` python
84
+ mkm = JupyAsyncMultiKernelManager('http://127.0.0.1:8812')
85
+ k1 = await mkm.ensure_kernel('analysis')
86
+ k2 = await mkm.ensure_kernel('analysis')
87
+ k1 == k2
88
+ ```
89
+
90
+ True
91
+
92
+ ``` python
93
+ await kc.aclose()
94
+ await km.aclose()
95
+ await mkm.shutdown_all()
96
+ await mkm.aclose()
97
+ ```
98
+
99
+ 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.
@@ -0,0 +1,7 @@
1
+ __version__ = '0.2.0'
2
+
3
+ from .core import JupyAsyncKernelClient, dumps, loads, serialize_binary_message, deserialize_binary_message
4
+ from .manager import JupyAsyncKernelManager, start_new_server_kernel
5
+ from .multimanager import JupyAsyncMultiKernelManager
6
+
7
+ __all__ = ["JupyAsyncKernelClient", "JupyAsyncKernelManager", "JupyAsyncMultiKernelManager", "start_new_server_kernel"]
@@ -0,0 +1,109 @@
1
+ # Autogenerated by nbdev
2
+
3
+ d = { 'settings': { 'branch': 'main',
4
+ 'doc_baseurl': '/jupyasyncclient',
5
+ 'doc_host': 'https://AnswerDotAI.github.io',
6
+ 'git_url': 'https://github.com/AnswerDotAI/jupyasyncclient',
7
+ 'lib_path': 'jupyasyncclient'},
8
+ 'syms': { 'jupyasyncclient.core': { 'jupyasyncclient.core.JupyAsyncKernelClient': ( 'core.html#jupyasynckernelclient',
9
+ 'jupyasyncclient/core.py'),
10
+ 'jupyasyncclient.core.JupyAsyncKernelClient.__init__': ( 'core.html#jupyasynckernelclient.__init__',
11
+ 'jupyasyncclient/core.py'),
12
+ 'jupyasyncclient.core.JupyAsyncKernelClient._ensure_started': ( 'core.html#jupyasynckernelclient._ensure_started',
13
+ 'jupyasyncclient/core.py'),
14
+ 'jupyasyncclient.core.JupyAsyncKernelClient._exec_req': ( 'core.html#jupyasynckernelclient._exec_req',
15
+ 'jupyasyncclient/core.py'),
16
+ 'jupyasyncclient.core.JupyAsyncKernelClient._fail_pending': ( 'core.html#jupyasynckernelclient._fail_pending',
17
+ 'jupyasyncclient/core.py'),
18
+ 'jupyasyncclient.core.JupyAsyncKernelClient._get_msg': ( 'core.html#jupyasynckernelclient._get_msg',
19
+ 'jupyasyncclient/core.py'),
20
+ 'jupyasyncclient.core.JupyAsyncKernelClient._kpath': ( 'core.html#jupyasynckernelclient._kpath',
21
+ 'jupyasyncclient/core.py'),
22
+ 'jupyasyncclient.core.JupyAsyncKernelClient._queue_msg': ( 'core.html#jupyasynckernelclient._queue_msg',
23
+ 'jupyasyncclient/core.py'),
24
+ 'jupyasyncclient.core.JupyAsyncKernelClient._reconnect': ( 'core.html#jupyasynckernelclient._reconnect',
25
+ 'jupyasyncclient/core.py'),
26
+ 'jupyasyncclient.core.JupyAsyncKernelClient._recv_loop': ( 'core.html#jupyasynckernelclient._recv_loop',
27
+ 'jupyasyncclient/core.py'),
28
+ 'jupyasyncclient.core.JupyAsyncKernelClient._route_reply_or_queue': ( 'core.html#jupyasynckernelclient._route_reply_or_queue',
29
+ 'jupyasyncclient/core.py'),
30
+ 'jupyasyncclient.core.JupyAsyncKernelClient._send_and_await_reply': ( 'core.html#jupyasynckernelclient._send_and_await_reply',
31
+ 'jupyasyncclient/core.py'),
32
+ 'jupyasyncclient.core.JupyAsyncKernelClient._send_loop': ( 'core.html#jupyasynckernelclient._send_loop',
33
+ 'jupyasyncclient/core.py'),
34
+ 'jupyasyncclient.core.JupyAsyncKernelClient._start_ws': ( 'core.html#jupyasynckernelclient._start_ws',
35
+ 'jupyasyncclient/core.py'),
36
+ 'jupyasyncclient.core.JupyAsyncKernelClient.aclose': ( 'core.html#jupyasynckernelclient.aclose',
37
+ 'jupyasyncclient/core.py'),
38
+ 'jupyasyncclient.core.JupyAsyncKernelClient.channels_running': ( 'core.html#jupyasynckernelclient.channels_running',
39
+ 'jupyasyncclient/core.py'),
40
+ 'jupyasyncclient.core.JupyAsyncKernelClient.comm_info': ( 'core.html#jupyasynckernelclient.comm_info',
41
+ 'jupyasyncclient/core.py'),
42
+ 'jupyasyncclient.core.JupyAsyncKernelClient.complete': ( 'core.html#jupyasynckernelclient.complete',
43
+ 'jupyasyncclient/core.py'),
44
+ 'jupyasyncclient.core.JupyAsyncKernelClient.create_subshell': ( 'core.html#jupyasynckernelclient.create_subshell',
45
+ 'jupyasyncclient/core.py'),
46
+ 'jupyasyncclient.core.JupyAsyncKernelClient.delete_subshell': ( 'core.html#jupyasynckernelclient.delete_subshell',
47
+ 'jupyasyncclient/core.py'),
48
+ 'jupyasyncclient.core.JupyAsyncKernelClient.delete_subshell_request': ( 'core.html#jupyasynckernelclient.delete_subshell_request',
49
+ 'jupyasyncclient/core.py'),
50
+ 'jupyasyncclient.core.JupyAsyncKernelClient.execute': ( 'core.html#jupyasynckernelclient.execute',
51
+ 'jupyasyncclient/core.py'),
52
+ 'jupyasyncclient.core.JupyAsyncKernelClient.get_control_msg': ( 'core.html#jupyasynckernelclient.get_control_msg',
53
+ 'jupyasyncclient/core.py'),
54
+ 'jupyasyncclient.core.JupyAsyncKernelClient.get_iopub_msg': ( 'core.html#jupyasynckernelclient.get_iopub_msg',
55
+ 'jupyasyncclient/core.py'),
56
+ 'jupyasyncclient.core.JupyAsyncKernelClient.get_shell_msg': ( 'core.html#jupyasynckernelclient.get_shell_msg',
57
+ 'jupyasyncclient/core.py'),
58
+ 'jupyasyncclient.core.JupyAsyncKernelClient.get_stdin_msg': ( 'core.html#jupyasynckernelclient.get_stdin_msg',
59
+ 'jupyasyncclient/core.py'),
60
+ 'jupyasyncclient.core.JupyAsyncKernelClient.history': ( 'core.html#jupyasynckernelclient.history',
61
+ 'jupyasyncclient/core.py'),
62
+ 'jupyasyncclient.core.JupyAsyncKernelClient.input': ( 'core.html#jupyasynckernelclient.input',
63
+ 'jupyasyncclient/core.py'),
64
+ 'jupyasyncclient.core.JupyAsyncKernelClient.inspect': ( 'core.html#jupyasynckernelclient.inspect',
65
+ 'jupyasyncclient/core.py'),
66
+ 'jupyasyncclient.core.JupyAsyncKernelClient.interrupt_kernel': ( 'core.html#jupyasynckernelclient.interrupt_kernel',
67
+ 'jupyasyncclient/core.py'),
68
+ 'jupyasyncclient.core.JupyAsyncKernelClient.is_alive': ( 'core.html#jupyasynckernelclient.is_alive',
69
+ 'jupyasyncclient/core.py'),
70
+ 'jupyasyncclient.core.JupyAsyncKernelClient.is_complete': ( 'core.html#jupyasynckernelclient.is_complete',
71
+ 'jupyasyncclient/core.py'),
72
+ 'jupyasyncclient.core.JupyAsyncKernelClient.kernel_info': ( 'core.html#jupyasynckernelclient.kernel_info',
73
+ 'jupyasyncclient/core.py'),
74
+ 'jupyasyncclient.core.JupyAsyncKernelClient.kernel_request': ( 'core.html#jupyasynckernelclient.kernel_request',
75
+ 'jupyasyncclient/core.py'),
76
+ 'jupyasyncclient.core.JupyAsyncKernelClient.list_subshell': ( 'core.html#jupyasynckernelclient.list_subshell',
77
+ 'jupyasyncclient/core.py'),
78
+ 'jupyasyncclient.core.JupyAsyncKernelClient.reply': ( 'core.html#jupyasynckernelclient.reply',
79
+ 'jupyasyncclient/core.py'),
80
+ 'jupyasyncclient.core.JupyAsyncKernelClient.restart_kernel': ( 'core.html#jupyasynckernelclient.restart_kernel',
81
+ 'jupyasyncclient/core.py'),
82
+ 'jupyasyncclient.core.JupyAsyncKernelClient.shutdown': ( 'core.html#jupyasynckernelclient.shutdown',
83
+ 'jupyasyncclient/core.py'),
84
+ 'jupyasyncclient.core.JupyAsyncKernelClient.shutdown_kernel': ( 'core.html#jupyasynckernelclient.shutdown_kernel',
85
+ 'jupyasyncclient/core.py'),
86
+ 'jupyasyncclient.core.JupyAsyncKernelClient.start_channels': ( 'core.html#jupyasynckernelclient.start_channels',
87
+ 'jupyasyncclient/core.py'),
88
+ 'jupyasyncclient.core.JupyAsyncKernelClient.start_kernel': ( 'core.html#jupyasynckernelclient.start_kernel',
89
+ 'jupyasyncclient/core.py'),
90
+ 'jupyasyncclient.core.JupyAsyncKernelClient.stop_channels': ( 'core.html#jupyasynckernelclient.stop_channels',
91
+ 'jupyasyncclient/core.py'),
92
+ 'jupyasyncclient.core.JupyAsyncKernelClient.wait_for_ready': ( 'core.html#jupyasynckernelclient.wait_for_ready',
93
+ 'jupyasyncclient/core.py'),
94
+ 'jupyasyncclient.core.KernelApi': ('core.html#kernelapi', 'jupyasyncclient/core.py'),
95
+ 'jupyasyncclient.core.KernelApi.__init__': ( 'core.html#kernelapi.__init__',
96
+ 'jupyasyncclient/core.py'),
97
+ 'jupyasyncclient.core.KernelApi._ensure_http': ( 'core.html#kernelapi._ensure_http',
98
+ 'jupyasyncclient/core.py'),
99
+ 'jupyasyncclient.core.KernelApi._kpath': ('core.html#kernelapi._kpath', 'jupyasyncclient/core.py'),
100
+ 'jupyasyncclient.core.KernelApi._request': ( 'core.html#kernelapi._request',
101
+ 'jupyasyncclient/core.py'),
102
+ 'jupyasyncclient.core.KernelApi.aclose_http': ( 'core.html#kernelapi.aclose_http',
103
+ 'jupyasyncclient/core.py'),
104
+ 'jupyasyncclient.core.KernelApi.kernel_request': ( 'core.html#kernelapi.kernel_request',
105
+ 'jupyasyncclient/core.py'),
106
+ 'jupyasyncclient.core._gen_request': ('core.html#_gen_request', 'jupyasyncclient/core.py'),
107
+ 'jupyasyncclient.core._join_url': ('core.html#_join_url', 'jupyasyncclient/core.py')},
108
+ 'jupyasyncclient.manager': {},
109
+ 'jupyasyncclient.multimanager': {}}}