async-kernel 0.13.3__tar.gz → 0.14.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.
- {async_kernel-0.13.3 → async_kernel-0.14.0}/CHANGELOG.md +15 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/PKG-INFO +30 -14
- {async_kernel-0.13.3 → async_kernel-0.14.0}/README.md +29 -13
- {async_kernel-0.13.3 → async_kernel-0.14.0}/_version.py +2 -2
- async_kernel-0.14.0/docs/reference/event_loop.md +9 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/usage/commands.md +18 -18
- {async_kernel-0.13.3 → async_kernel-0.14.0}/pyproject.toml +1 -1
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/caller.py +14 -14
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/event_loop/asyncio_guest.py +1 -1
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/event_loop/qt_host.py +5 -3
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/event_loop/run.py +27 -28
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/event_loop/tk_host.py +14 -11
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/interface/base.py +2 -2
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/interface/zmq.py +6 -6
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/typing.py +7 -22
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_caller.py +3 -3
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_command.py +8 -8
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_event_loop.py +83 -8
- {async_kernel-0.13.3 → async_kernel-0.14.0}/uv.lock +25 -2
- async_kernel-0.13.3/docs/reference/event_loop.md +0 -1
- async_kernel-0.13.3/src/async_kernel/event_loop/asyncio_host.py +0 -41
- async_kernel-0.13.3/src/async_kernel/event_loop/trio_host.py +0 -47
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/dependabot.yaml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/release.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/ci.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/enforce-label.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/new_release.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/pre-commit.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/publish-docs.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.gitignore +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.pre-commit-config.yaml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.vscode/launch.json +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.vscode/settings.json +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/.vscode/spellright.dict +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/CONTRIBUTING.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/IPYTHON_LICENSE +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/LICENSE +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/cliff.toml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/about/changelog.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/about/contributing.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/about/index.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/about/license.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/index.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/javascripts/extra.js +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/notebooks/caller.ipynb +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/notebooks/concurrency.ipynb +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/overrides/main.html +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/asyncshell.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/caller.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/comm.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/command.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/common.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/debugger.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/index.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/interface.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/kernel.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/kernelspec.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/pending.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/typing.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/reference/utils.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/stylesheets/extra.css +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/docs/usage/index.md +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/hatch_build.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/mkdocs.yml +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/__init__.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/__main__.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/asyncshell.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/comm.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/command.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/common.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/compiler.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/debugger.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/event_loop/__init__.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/interface/__init__.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/interface/callable.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/iostream.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/kernel.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/kernelspec.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/pending.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/py.typed +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/resources/logo-32x32.png +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/resources/logo-64x64.png +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/resources/logo-svg.svg +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/src/async_kernel/utils.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/__init__.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/conftest.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/references.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_callable_kernel_interface.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_comm.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_common.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_debugger.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_enter_kernel.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_iostream.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_kernel.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_kernel_subclass.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_kernelspec.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_message_spec.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_pending.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_typing.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_utils.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/test_zmq_messaging.py +0 -0
- {async_kernel-0.13.3 → async_kernel-0.14.0}/tests/utils.py +0 -0
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.14.0] - 2026-03-24
|
|
9
|
+
|
|
10
|
+
### <!-- 0 --> 🏗️ Breaking changes
|
|
11
|
+
|
|
12
|
+
- Rename loop to host and Loop to Hosts [#370](https://github.com/fleming79/async-kernel/pull/370)
|
|
13
|
+
|
|
14
|
+
### <!-- 5 --> 📝 Documentation
|
|
15
|
+
|
|
16
|
+
- Update event loop documentation. [#372](https://github.com/fleming79/async-kernel/pull/372)
|
|
17
|
+
|
|
18
|
+
- Update readme [#371](https://github.com/fleming79/async-kernel/pull/371)
|
|
19
|
+
|
|
8
20
|
## [0.13.3] - 2026-03-23
|
|
9
21
|
|
|
10
22
|
### <!-- 0 --> 🏗️ Breaking changes
|
|
@@ -15,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
27
|
|
|
16
28
|
### <!-- 6 --> 🌀 Miscellaneous
|
|
17
29
|
|
|
30
|
+
- Prepare for release v0.13.3 [#369](https://github.com/fleming79/async-kernel/pull/369)
|
|
31
|
+
|
|
18
32
|
- Minor SingleConsumerAsyncQueue optimization. [#368](https://github.com/fleming79/async-kernel/pull/368)
|
|
19
33
|
|
|
20
34
|
- Fixtest_command_start_kernel_enable_matplotlib [#367](https://github.com/fleming79/async-kernel/pull/367)
|
|
@@ -1023,6 +1037,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1023
1037
|
|
|
1024
1038
|
- Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
|
|
1025
1039
|
|
|
1040
|
+
[0.14.0]: https://github.com/fleming79/async-kernel/compare/v0.13.3..v0.14.0
|
|
1026
1041
|
[0.13.3]: https://github.com/fleming79/async-kernel/compare/v0.13.2..v0.13.3
|
|
1027
1042
|
[0.13.2]: https://github.com/fleming79/async-kernel/compare/v0.13.1..v0.13.2
|
|
1028
1043
|
[0.13.1]: https://github.com/fleming79/async-kernel/compare/v0.13.0..v0.13.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-kernel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: A concurrent python kernel for Jupyter supporting AnyIO, AsyncIO and Trio.
|
|
5
5
|
Project-URL: Homepage, https://fleming79.github.io/async-kernel
|
|
6
6
|
Project-URL: Documentation, https://fleming79.github.io/async-kernel
|
|
@@ -71,6 +71,7 @@ The kernel provides two external interfaces:
|
|
|
71
71
|
## Highlights
|
|
72
72
|
|
|
73
73
|
- [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
|
|
74
|
+
- top-level await ('asyncio' or 'trio' backend) in cells
|
|
74
75
|
- [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
|
|
75
76
|
- [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
|
|
76
77
|
- [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
|
|
@@ -85,9 +86,8 @@ The kernel provides two external interfaces:
|
|
|
85
86
|
- [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
|
|
86
87
|
|
|
87
88
|
[^1]:
|
|
88
|
-
A gui (_host_) enabled kernel
|
|
89
|
-
|
|
90
|
-
deliberate design choice to to ensure good performance and reliability.
|
|
89
|
+
A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
|
|
90
|
+
the backend as a guest, then finally the Kernel is started.
|
|
91
91
|
|
|
92
92
|
[^2]:
|
|
93
93
|
The asyncio implementation of `start_guest_run` was written by
|
|
@@ -105,24 +105,40 @@ Another problem exists when an asynchronous execute request awaits a result that
|
|
|
105
105
|
via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
|
|
106
106
|
the _blocking_ execute request[^5].
|
|
107
107
|
|
|
108
|
-
async-kernel handles messages according to the channel
|
|
108
|
+
async-kernel handles messages according to the channel and message type. So widget com message
|
|
109
109
|
will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
|
|
110
110
|
|
|
111
111
|
#### Example
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
Make a blocking call in a Jupyter lab notebook or console.
|
|
114
114
|
|
|
115
115
|
```python
|
|
116
|
-
# Make the shell thread busy
|
|
116
|
+
# Make the shell's thread busy
|
|
117
117
|
import time
|
|
118
118
|
|
|
119
119
|
time.sleep(1e6)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
While the above is _blocking_ (the kernel is _busy_).
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
```python
|
|
125
|
+
dir() # try code completion (tab) or view the docstring (shift tab)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Interrupt the kernel.
|
|
129
|
+
|
|
130
|
+
It also works for awaitables.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import ipywidgets as ipw
|
|
134
|
+
from aiologic import Event
|
|
135
|
+
|
|
136
|
+
b = ipw.Button(description="Click me")
|
|
137
|
+
event = Event()
|
|
138
|
+
b.on_click(lambda _: event.set())
|
|
139
|
+
display(b)
|
|
140
|
+
await event
|
|
141
|
+
```
|
|
126
142
|
|
|
127
143
|
[^5]:
|
|
128
144
|
IPyKernel _solves_ this issue specifically for widgets by using the concept of
|
|
@@ -165,11 +181,11 @@ The kernel can be started with a gui event loop as the _host_ and the _backend_
|
|
|
165
181
|
|
|
166
182
|
```bash
|
|
167
183
|
# tk
|
|
168
|
-
async-kernel -a async-tk --interface.
|
|
184
|
+
async-kernel -a async-tk --interface.host=tk
|
|
169
185
|
|
|
170
186
|
# qt
|
|
171
187
|
pip install PySide6-Essentials
|
|
172
|
-
async-kernel -a async-qt --interface.
|
|
188
|
+
async-kernel -a async-qt --interface.host=qt
|
|
173
189
|
```
|
|
174
190
|
|
|
175
191
|
#### trio backend
|
|
@@ -177,11 +193,11 @@ async-kernel -a async-qt --interface.loop=qt
|
|
|
177
193
|
```bash
|
|
178
194
|
pip install trio
|
|
179
195
|
# tk
|
|
180
|
-
async-kernel -a async-tk --interface.
|
|
196
|
+
async-kernel -a async-tk --interface.host=tk --interface.backend=trio
|
|
181
197
|
|
|
182
198
|
# qt
|
|
183
199
|
pip install PySide6-Essentials
|
|
184
|
-
async-kernel -a async-qt --interface.
|
|
200
|
+
async-kernel -a async-qt --interface.host=qt --interface.backend=trio
|
|
185
201
|
```
|
|
186
202
|
|
|
187
203
|
For further detail about kernel spec customisation see [command line and kernel configuration](https://fleming79.github.io/async-kernel/latest/usage/commands/).
|
|
@@ -22,6 +22,7 @@ The kernel provides two external interfaces:
|
|
|
22
22
|
## Highlights
|
|
23
23
|
|
|
24
24
|
- [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
|
|
25
|
+
- top-level await ('asyncio' or 'trio' backend) in cells
|
|
25
26
|
- [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
|
|
26
27
|
- [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
|
|
27
28
|
- [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
|
|
@@ -36,9 +37,8 @@ The kernel provides two external interfaces:
|
|
|
36
37
|
- [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
|
|
37
38
|
|
|
38
39
|
[^1]:
|
|
39
|
-
A gui (_host_) enabled kernel
|
|
40
|
-
|
|
41
|
-
deliberate design choice to to ensure good performance and reliability.
|
|
40
|
+
A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
|
|
41
|
+
the backend as a guest, then finally the Kernel is started.
|
|
42
42
|
|
|
43
43
|
[^2]:
|
|
44
44
|
The asyncio implementation of `start_guest_run` was written by
|
|
@@ -56,24 +56,40 @@ Another problem exists when an asynchronous execute request awaits a result that
|
|
|
56
56
|
via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
|
|
57
57
|
the _blocking_ execute request[^5].
|
|
58
58
|
|
|
59
|
-
async-kernel handles messages according to the channel
|
|
59
|
+
async-kernel handles messages according to the channel and message type. So widget com message
|
|
60
60
|
will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
|
|
61
61
|
|
|
62
62
|
#### Example
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
Make a blocking call in a Jupyter lab notebook or console.
|
|
65
65
|
|
|
66
66
|
```python
|
|
67
|
-
# Make the shell thread busy
|
|
67
|
+
# Make the shell's thread busy
|
|
68
68
|
import time
|
|
69
69
|
|
|
70
70
|
time.sleep(1e6)
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
While the above is _blocking_ (the kernel is _busy_).
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
```python
|
|
76
|
+
dir() # try code completion (tab) or view the docstring (shift tab)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Interrupt the kernel.
|
|
80
|
+
|
|
81
|
+
It also works for awaitables.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import ipywidgets as ipw
|
|
85
|
+
from aiologic import Event
|
|
86
|
+
|
|
87
|
+
b = ipw.Button(description="Click me")
|
|
88
|
+
event = Event()
|
|
89
|
+
b.on_click(lambda _: event.set())
|
|
90
|
+
display(b)
|
|
91
|
+
await event
|
|
92
|
+
```
|
|
77
93
|
|
|
78
94
|
[^5]:
|
|
79
95
|
IPyKernel _solves_ this issue specifically for widgets by using the concept of
|
|
@@ -116,11 +132,11 @@ The kernel can be started with a gui event loop as the _host_ and the _backend_
|
|
|
116
132
|
|
|
117
133
|
```bash
|
|
118
134
|
# tk
|
|
119
|
-
async-kernel -a async-tk --interface.
|
|
135
|
+
async-kernel -a async-tk --interface.host=tk
|
|
120
136
|
|
|
121
137
|
# qt
|
|
122
138
|
pip install PySide6-Essentials
|
|
123
|
-
async-kernel -a async-qt --interface.
|
|
139
|
+
async-kernel -a async-qt --interface.host=qt
|
|
124
140
|
```
|
|
125
141
|
|
|
126
142
|
#### trio backend
|
|
@@ -128,11 +144,11 @@ async-kernel -a async-qt --interface.loop=qt
|
|
|
128
144
|
```bash
|
|
129
145
|
pip install trio
|
|
130
146
|
# tk
|
|
131
|
-
async-kernel -a async-tk --interface.
|
|
147
|
+
async-kernel -a async-tk --interface.host=tk --interface.backend=trio
|
|
132
148
|
|
|
133
149
|
# qt
|
|
134
150
|
pip install PySide6-Essentials
|
|
135
|
-
async-kernel -a async-qt --interface.
|
|
151
|
+
async-kernel -a async-qt --interface.host=qt --interface.backend=trio
|
|
136
152
|
```
|
|
137
153
|
|
|
138
154
|
For further detail about kernel spec customisation see [command line and kernel configuration](https://fleming79.github.io/async-kernel/latest/usage/commands/).
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.14.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 14, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -31,7 +31,7 @@ The kernel spec looks like this:
|
|
|
31
31
|
"--start_interface=async_kernel.interface.start_kernel_zmq_interface",
|
|
32
32
|
"--kernel_name=async",
|
|
33
33
|
"--backend=trio",
|
|
34
|
-
"--
|
|
34
|
+
"--host=tk"
|
|
35
35
|
],
|
|
36
36
|
"env": {},
|
|
37
37
|
"display_name": "Async python tk trio",
|
|
@@ -53,8 +53,8 @@ A single kernel spec is created in the folder `<sys.prefix>/share/jupyter/kernel
|
|
|
53
53
|
- display_name: 'async'
|
|
54
54
|
- backend: `asyncio`
|
|
55
55
|
- backend_options: `{'use_uvloop':True}` if uvloop or winloop is installed
|
|
56
|
-
-
|
|
57
|
-
-
|
|
56
|
+
- host: `None`
|
|
57
|
+
- host_options: `None`
|
|
58
58
|
|
|
59
59
|
The kernel spec can be updated by adding a kernel spec with the same name ('async').
|
|
60
60
|
|
|
@@ -63,8 +63,8 @@ The kernel spec can be updated by adding a kernel spec with the same name ('asyn
|
|
|
63
63
|
There are two supported backends 'asyncio' and 'trio'.
|
|
64
64
|
|
|
65
65
|
In CPython the [backend][async_kernel.typing.Backend] is started using [anyio.run][].
|
|
66
|
-
The type of backend can be specified at the attribute [
|
|
67
|
-
The backend_options can be specified at the attribute [
|
|
66
|
+
The type of backend can be specified at the attribute [interface.backend][async_kernel.interface.BaseKernelInterface.backend].
|
|
67
|
+
The backend_options can be specified at the attribute [interface.backend_options][async_kernel.interface.zmq.ZMQKernelInterface.backend_options].
|
|
68
68
|
Options can be written as a literal python string.
|
|
69
69
|
|
|
70
70
|
```console
|
|
@@ -78,7 +78,7 @@ searches for the kernel specs.
|
|
|
78
78
|
|
|
79
79
|
The path where the kernel spec is installed/deleted can also be specified by either `prefix` or `folder`.
|
|
80
80
|
|
|
81
|
-
**
|
|
81
|
+
**Options**
|
|
82
82
|
|
|
83
83
|
- prefix (optional): the prefix to use with `PREFIX/share/jupyter/kernels` (defaults is [sys.prefix][]).
|
|
84
84
|
- folder (optional) the full path to the `kernels` folder.
|
|
@@ -110,39 +110,39 @@ Below are some example kernel specs for host and backend kernel specs.
|
|
|
110
110
|
|
|
111
111
|
```console
|
|
112
112
|
# tk host asyncio backend
|
|
113
|
-
async-kernel -a async-tk --interface.
|
|
113
|
+
async-kernel -a async-tk --interface.host=tk
|
|
114
114
|
|
|
115
115
|
# tk host trio backend
|
|
116
|
-
async-kernel -a async-tk --interface.
|
|
116
|
+
async-kernel -a async-tk --interface.host=tk --backend=trio
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
### qt
|
|
120
120
|
|
|
121
121
|
```console
|
|
122
122
|
# qt host asyncio backend
|
|
123
|
-
async-kernel -a async-qt --interface.
|
|
123
|
+
async-kernel -a async-qt --interface.host=qt
|
|
124
124
|
|
|
125
125
|
# qt host trio backend
|
|
126
|
-
async-kernel -a async-qt-trio --interface.
|
|
126
|
+
async-kernel -a async-qt-trio --interface.host=qt --interface.backend=trio
|
|
127
127
|
|
|
128
|
-
# PySide6 is default. You can specify a different module via `
|
|
129
|
-
async-kernel -a async-qt --interface.
|
|
128
|
+
# PySide6 is default. You can specify a different module via `host_options`
|
|
129
|
+
async-kernel -a async-qt --interface.host=qt --interface.host_options={'module':'PySide2'}
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
##
|
|
132
|
+
## Host options
|
|
133
133
|
|
|
134
|
-
Options can be provided to configure how a
|
|
134
|
+
Options can be provided to configure how a host loads. There are only a few options available
|
|
135
135
|
at present.
|
|
136
136
|
|
|
137
137
|
- `host_class' `[type[Host| str]]` : A customised subclass of a [Host][async_kernel.event_loop.run.Host]
|
|
138
138
|
or a dotted import path to the customised Host.
|
|
139
|
-
- `'module': The module name on which to base the event loop. (Only applies to
|
|
139
|
+
- `'module': The module name on which to base the event loop. (Only applies to [qt][async_kernel.event_loop.qt_host.QtHost]).
|
|
140
140
|
|
|
141
141
|
## Backend options
|
|
142
142
|
|
|
143
143
|
Options can be provided for how the backend is started.
|
|
144
144
|
|
|
145
|
-
- With a (gui)
|
|
145
|
+
- With a (gui) host: Options for `start_guest_run`
|
|
146
146
|
- [trio.lowlevel.start_guest_run][]
|
|
147
147
|
- asyncio
|
|
148
148
|
- host_uses_signal_set_wakeup_fd
|
|
@@ -151,11 +151,11 @@ Options can be provided for how the backend is started.
|
|
|
151
151
|
- task_factory,
|
|
152
152
|
- context,
|
|
153
153
|
- debug,
|
|
154
|
-
- Without a (gui)
|
|
154
|
+
- Without a (gui) host: `backend_options` in [anyio.run][]
|
|
155
155
|
|
|
156
156
|
```console
|
|
157
157
|
# If uvloop is installed it will be used by default. You can do this to disable it.
|
|
158
|
-
async-kernel -a async "--interface.
|
|
158
|
+
async-kernel -a async "--interface.host_options={'use_uvloop':False}"
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
### Custom arguments
|
|
@@ -30,7 +30,7 @@ from async_kernel import utils
|
|
|
30
30
|
from async_kernel.common import Fixed
|
|
31
31
|
from async_kernel.event_loop.run import Host, get_start_guest_run
|
|
32
32
|
from async_kernel.pending import Pending, PendingCancelled, PendingGroup, PendingManager, PendingTracker
|
|
33
|
-
from async_kernel.typing import Backend, CallerCreateOptions, CallerState,
|
|
33
|
+
from async_kernel.typing import Backend, CallerCreateOptions, CallerState, Hosts, NoValue, RunSettings, T
|
|
34
34
|
|
|
35
35
|
with contextlib.suppress(ImportError):
|
|
36
36
|
# Monkey patch sniffio.current_async_library` with aiologic's version which does a better job.
|
|
@@ -211,8 +211,8 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
211
211
|
_idle_time: float = 0.0
|
|
212
212
|
_backend: Backend
|
|
213
213
|
_backend_options: dict[str, Any] | None
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
_host: Hosts | None
|
|
215
|
+
_host_options: dict[str, Any] | None
|
|
216
216
|
_protected = False
|
|
217
217
|
_use_safe_checkpoint = False
|
|
218
218
|
_state: CallerState = CallerState.initial
|
|
@@ -270,14 +270,14 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
270
270
|
return self._backend_options
|
|
271
271
|
|
|
272
272
|
@property
|
|
273
|
-
def
|
|
273
|
+
def host(self) -> Hosts | None:
|
|
274
274
|
"The gui event loop if there is one."
|
|
275
|
-
return self.
|
|
275
|
+
return self._host
|
|
276
276
|
|
|
277
277
|
@property
|
|
278
|
-
def
|
|
278
|
+
def host_options(self) -> dict | None:
|
|
279
279
|
"Options used to create the gui event loop."
|
|
280
|
-
return self.
|
|
280
|
+
return self._host_options
|
|
281
281
|
|
|
282
282
|
@property
|
|
283
283
|
def protected(self) -> bool:
|
|
@@ -320,7 +320,7 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
320
320
|
return {
|
|
321
321
|
"name": self._name,
|
|
322
322
|
"backend": str(self._backend),
|
|
323
|
-
"
|
|
323
|
+
"host": self._host,
|
|
324
324
|
"thread": self._thread.name,
|
|
325
325
|
"id": self._caller_id,
|
|
326
326
|
}
|
|
@@ -393,9 +393,9 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
393
393
|
inst = super().__new__(cls)
|
|
394
394
|
inst._name = name
|
|
395
395
|
inst._backend = Backend(backend or current_async_library())
|
|
396
|
-
inst.
|
|
396
|
+
inst._host = Hosts(loop) if (loop := kwargs.get("host")) else None
|
|
397
397
|
inst._backend_options = kwargs.get("backend_options")
|
|
398
|
-
inst.
|
|
398
|
+
inst._host_options = kwargs.get("host_options")
|
|
399
399
|
inst._protected = kwargs.get("protected", False)
|
|
400
400
|
inst._zmq_context = kwargs.get("zmq_context")
|
|
401
401
|
inst.log = kwargs.get("log") or logging.LoggerAdapter(logging.getLogger())
|
|
@@ -454,9 +454,9 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
454
454
|
name = self.name or "async_kernel_caller"
|
|
455
455
|
settings = RunSettings(
|
|
456
456
|
backend=self.backend,
|
|
457
|
-
|
|
457
|
+
host=self.host,
|
|
458
458
|
backend_options=self.backend_options,
|
|
459
|
-
|
|
459
|
+
host_options=self.host_options,
|
|
460
460
|
)
|
|
461
461
|
args = [run_caller_in_context, (), settings]
|
|
462
462
|
self._thread = threading.Thread(target=async_kernel.event_loop.run, args=args, name=name, daemon=True)
|
|
@@ -713,8 +713,8 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
713
713
|
if (backend := kwargs.get("backend")) and caller.backend != backend:
|
|
714
714
|
msg = f"Backend mismatch! {backend=} {caller.backend=}"
|
|
715
715
|
raise RuntimeError(msg)
|
|
716
|
-
if (
|
|
717
|
-
msg = f"
|
|
716
|
+
if (host := kwargs.get("host")) and caller.host != host:
|
|
717
|
+
msg = f"Host mismatch! {host=} {caller.host=}"
|
|
718
718
|
raise RuntimeError(msg)
|
|
719
719
|
return caller
|
|
720
720
|
if "backend" not in kwargs:
|
|
@@ -24,7 +24,7 @@ from typing import TYPE_CHECKING, Any, Literal
|
|
|
24
24
|
from aiologic.meta import import_from
|
|
25
25
|
from typing_extensions import override
|
|
26
26
|
|
|
27
|
-
from async_kernel.typing import
|
|
27
|
+
from async_kernel.typing import Hosts, T
|
|
28
28
|
|
|
29
29
|
from .run import Host
|
|
30
30
|
|
|
@@ -33,12 +33,14 @@ if TYPE_CHECKING:
|
|
|
33
33
|
|
|
34
34
|
from PySide6 import QtCore, QtWidgets # noqa: TC004
|
|
35
35
|
|
|
36
|
+
__all__ = ["QtHost"]
|
|
37
|
+
|
|
36
38
|
|
|
37
39
|
class QtHost(Host[T]):
|
|
38
|
-
|
|
40
|
+
HOST = Hosts.qt
|
|
39
41
|
MATPLOTLIB_GUIS = ("qt",)
|
|
40
42
|
|
|
41
|
-
def __init__(self, module:
|
|
43
|
+
def __init__(self, module: Literal["PySide6", "PySide2", "PyQt5", "PyQt6"] = "PySide6") -> None:
|
|
42
44
|
if threading.current_thread() is not threading.main_thread():
|
|
43
45
|
msg = "QT can only be run in main thread!"
|
|
44
46
|
raise RuntimeError(msg)
|
|
@@ -8,7 +8,7 @@ import anyio
|
|
|
8
8
|
from wrapt import lazy_import
|
|
9
9
|
|
|
10
10
|
from async_kernel.common import import_item
|
|
11
|
-
from async_kernel.typing import Backend,
|
|
11
|
+
from async_kernel.typing import Backend, Hosts, RunSettings, T
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from collections.abc import Callable
|
|
@@ -39,13 +39,13 @@ def get_start_guest_run(backend: Backend):
|
|
|
39
39
|
def run(func: Callable[..., CoroutineType[Any, Any, T]], args: tuple, settings: RunSettings, /) -> T:
|
|
40
40
|
"""
|
|
41
41
|
Run `func` to completion asynchronously in the current thread using a [backend][async_kernel.typing.Backend]
|
|
42
|
-
with an optional gui event loop
|
|
42
|
+
with an optional host (gui event loop).
|
|
43
43
|
|
|
44
44
|
The default backend is ['asyncio'][async_kernel.typing.Backend.asyncio].
|
|
45
45
|
|
|
46
|
-
If [
|
|
46
|
+
If [host][async_kernel.typing.Hosts] is specified in `settings`. A _host_ (gui) mainloop
|
|
47
47
|
will be started with the `backend` running as a guest (in the same thread). The `backend`
|
|
48
|
-
will execute `func` asynchronously to completion. Once completed the backend and host
|
|
48
|
+
will execute `func` asynchronously to completion. Once completed the `backend` and `host`
|
|
49
49
|
are stopped and finally the result is returned.
|
|
50
50
|
|
|
51
51
|
Args:
|
|
@@ -53,13 +53,12 @@ def run(func: Callable[..., CoroutineType[Any, Any, T]], args: tuple, settings:
|
|
|
53
53
|
args: Args to use when calling func.
|
|
54
54
|
settings: Settings to use when running func.
|
|
55
55
|
|
|
56
|
-
Custom
|
|
57
|
-
A custom
|
|
58
|
-
|
|
59
|
-
can be the class or a dotted path if it is importable.
|
|
56
|
+
Custom host:
|
|
57
|
+
A custom host can be started by subclassing [Host][] and passed as the 'host_class' as the
|
|
58
|
+
class or a dotted path if it is importable.
|
|
60
59
|
"""
|
|
61
|
-
if settings.get("
|
|
62
|
-
# A
|
|
60
|
+
if settings.get("host"):
|
|
61
|
+
# A gui with the backend running as a guest.
|
|
63
62
|
return Host.run(func, args, settings)
|
|
64
63
|
# backend only.
|
|
65
64
|
return anyio.run(
|
|
@@ -79,12 +78,12 @@ def get_runtime_matplotlib_guis(thread: threading.Thread | None = None) -> tuple
|
|
|
79
78
|
|
|
80
79
|
class Host(Generic[T]):
|
|
81
80
|
"""
|
|
82
|
-
A class that provides the necessary callbacks
|
|
81
|
+
A class that provides the necessary callbacks to run a gui event loop with a `backend` started using `start_guest_run`.
|
|
83
82
|
"""
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
HOST: Hosts
|
|
86
85
|
MATPLOTLIB_GUIS = ()
|
|
87
|
-
_subclasses: dict[
|
|
86
|
+
_subclasses: dict[Hosts, type[Self]] = {}
|
|
88
87
|
_instances: dict[threading.Thread, Host] = {}
|
|
89
88
|
|
|
90
89
|
_outcome: Outcome[T] | None = None
|
|
@@ -92,8 +91,8 @@ class Host(Generic[T]):
|
|
|
92
91
|
"A callback to start the guest. This must be called by a subclass."
|
|
93
92
|
|
|
94
93
|
def __init_subclass__(cls) -> None:
|
|
95
|
-
if cls.
|
|
96
|
-
cls._subclasses[cls.
|
|
94
|
+
if cls.HOST is not Hosts.custom:
|
|
95
|
+
cls._subclasses[cls.HOST] = cls
|
|
97
96
|
|
|
98
97
|
@classmethod
|
|
99
98
|
def current(cls, thread: threading.Thread | None = None) -> Host | None:
|
|
@@ -109,28 +108,28 @@ class Host(Generic[T]):
|
|
|
109
108
|
msg = "A host is already running in this thread"
|
|
110
109
|
raise RuntimeError(msg)
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
host = Hosts(settings.get("host"))
|
|
113
112
|
backend = Backend(settings.get("backend", "asyncio"))
|
|
114
113
|
backend_options = settings.get("backend_options") or {}
|
|
115
|
-
|
|
114
|
+
host_options = settings.get("host_options") or {}
|
|
116
115
|
|
|
117
|
-
if "host_class" in
|
|
118
|
-
|
|
119
|
-
cls_ =
|
|
116
|
+
if "host_class" in host_options:
|
|
117
|
+
host_options = host_options.copy()
|
|
118
|
+
cls_ = host_options.pop("host_class")
|
|
120
119
|
if isinstance(cls_, str):
|
|
121
120
|
cls_ = import_item(cls_)
|
|
122
121
|
if not issubclass(cls_, cls):
|
|
123
122
|
msg = f"{cls_} is not a subclass of {cls}!"
|
|
124
123
|
raise TypeError(msg)
|
|
125
124
|
else:
|
|
126
|
-
assert
|
|
127
|
-
if
|
|
128
|
-
import_module(f"async_kernel.event_loop.{
|
|
129
|
-
assert
|
|
130
|
-
cls_ = cls._subclasses[
|
|
131
|
-
assert cls_.
|
|
132
|
-
|
|
133
|
-
host = cls_(**
|
|
125
|
+
assert host != backend
|
|
126
|
+
if host not in cls._subclasses:
|
|
127
|
+
import_module(f"async_kernel.event_loop.{host}_host")
|
|
128
|
+
assert host in cls._subclasses, f"Host for {host=} is not implemented correctly!"
|
|
129
|
+
cls_ = cls._subclasses[host]
|
|
130
|
+
assert cls_.HOST is host
|
|
131
|
+
|
|
132
|
+
host = cls_(**host_options)
|
|
134
133
|
# set the `start_guest` function (runs once).
|
|
135
134
|
backend_options.setdefault("host_uses_signal_set_wakeup_fd", host.host_uses_signal_set_wakeup_fd)
|
|
136
135
|
start_guest_run = get_start_guest_run(backend)
|