async-kernel 0.22.0__tar.gz → 0.22.1__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.22.0 → async_kernel-0.22.1}/CHANGELOG.md +17 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/PKG-INFO +1 -1
- {async_kernel-0.22.0 → async_kernel-0.22.1}/_version.py +2 -2
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/caller.py +1 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/zmq_poll.py +40 -25
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/interface/base.py +0 -9
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/interface/callable.py +11 -10
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/messaging/base.py +7 -3
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/messaging/zmq.py +2 -2
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_callable_interface.py +1 -3
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_eventloop_zmq_poll.py +5 -5
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/dependabot.yaml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/release.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/ci.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/enforce-label.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/new_release.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/pre-commit.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/publish-docs.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.github/workflows/publish-to-pypi.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.gitignore +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.pre-commit-config.yaml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.vscode/launch.json +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.vscode/settings.json +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/.vscode/spellright.dict +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/CONTRIBUTING.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/IPYTHON_LICENSE +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/LICENSE +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/README.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/cliff.toml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/about/changelog.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/about/contributing.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/about/index.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/about/license.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/index.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/javascripts/extra.js +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/notebooks/caller.ipynb +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/notebooks/concurrency.ipynb +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/notebooks/custom_kernel.ipynb +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/overrides/main.html +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/caller.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/comm.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/command.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/common.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/debugger.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/event_loop.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/index.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/interface.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/ipshell.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/kernel.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/kernelspec.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/messaging.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/pending.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/shell.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/typing.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/reference/utils.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/stylesheets/extra.css +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/thread_safety.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/usage/commands.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/docs/usage/index.md +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/hatch_build.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/mkdocs.yml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/pyproject.toml +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/__main__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/comm.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/command.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/common.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/compat/attr_docs.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/compat/json.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/compiler.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/debugger.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/qt_host.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/run.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/tk_host.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/interface/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/interface/ip_app.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/kernel.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/kernelspec.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/messaging/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/outstream.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/pending.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/py.typed +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-32x32.png +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-64x64.png +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-svg.svg +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/shell/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/shell/base.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/shell/ipshell.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/typing.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/src/async_kernel/utils.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/__init__.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/conftest.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/references.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_caller.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_comm.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_command.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_common.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_compat.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_connection.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_debugger.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_debugger_static.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_enter_kernel.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_event_loop.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_interface.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_ipapp.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_kernel_ipshell.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_kernelspec.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_message_spec.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_outstream.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_pending.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_subclass.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_typing.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_utils.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_zmq_connection.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/test_zmq_subprocess.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/tests/utils.py +0 -0
- {async_kernel-0.22.0 → async_kernel-0.22.1}/uv.lock +0 -0
|
@@ -5,6 +5,20 @@ 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.22.1] - 2026-08-18
|
|
9
|
+
|
|
10
|
+
### <!-- 0 --> 🏗️ Breaking changes
|
|
11
|
+
|
|
12
|
+
- Update zmq_poll docstrings. [#567](https://github.com/fleming79/async-kernel/pull/567)
|
|
13
|
+
|
|
14
|
+
### <!-- 5 --> 📝 Documentation
|
|
15
|
+
|
|
16
|
+
- ZMQPoll typing and doc fixes. [#568](https://github.com/fleming79/async-kernel/pull/568)
|
|
17
|
+
|
|
18
|
+
### <!-- 6 --> 🌀 Miscellaneous
|
|
19
|
+
|
|
20
|
+
- Refactor create_interface_messge_callback_handler to only use one connection [#569](https://github.com/fleming79/async-kernel/pull/569)
|
|
21
|
+
|
|
8
22
|
## [0.22.0] - 2026-08-17
|
|
9
23
|
|
|
10
24
|
### <!-- 0 --> 🏗️ Breaking changes
|
|
@@ -37,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
37
51
|
|
|
38
52
|
### <!-- 6 --> 🌀 Miscellaneous
|
|
39
53
|
|
|
54
|
+
- Prepare for release v0.22.0 [#566](https://github.com/fleming79/async-kernel/pull/566)
|
|
55
|
+
|
|
40
56
|
- Bump uv from 0.11.32 to 0.12.4 [#562](https://github.com/fleming79/async-kernel/pull/562)
|
|
41
57
|
|
|
42
58
|
- Bump astral-sh/setup-uv from 9.0.0 to 10.0.1 in the actions group [#559](https://github.com/fleming79/async-kernel/pull/559)
|
|
@@ -1529,6 +1545,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1529
1545
|
|
|
1530
1546
|
- Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
|
|
1531
1547
|
|
|
1548
|
+
[0.22.1]: https://github.com/fleming79/async-kernel/compare/v0.22.0..v0.22.1
|
|
1532
1549
|
[0.22.0]: https://github.com/fleming79/async-kernel/compare/v0.21.0..v0.22.0
|
|
1533
1550
|
[0.21.0]: https://github.com/fleming79/async-kernel/compare/v0.20.2..v0.21.0
|
|
1534
1551
|
[0.20.2]: https://github.com/fleming79/async-kernel/compare/v0.20.1..v0.20.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: async-kernel
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.1
|
|
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
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.22.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 22,
|
|
21
|
+
__version__ = version = '0.22.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 22, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -14,7 +14,7 @@ from typing import TYPE_CHECKING, Any, Literal, Self
|
|
|
14
14
|
import zmq
|
|
15
15
|
from aiologic import BinarySemaphore, BusyResourceError
|
|
16
16
|
from typing_extensions import override
|
|
17
|
-
from zmq.backend import zmq_poll
|
|
17
|
+
from zmq.backend import zmq_poll as _zmq_poll
|
|
18
18
|
|
|
19
19
|
from async_kernel import Caller, utils
|
|
20
20
|
from async_kernel.common import Fixed
|
|
@@ -144,19 +144,30 @@ class ZMQPollSocket(zmq.sugar.Socket[bytes]):
|
|
|
144
144
|
self.zmq_poll.execute(super().unsubscribe, topic)
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
T_key = tuple[
|
|
147
|
+
T_key = tuple[ZMQPollSocket, int]
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
class ZMQPoll:
|
|
151
151
|
"""A [zmq_poll](https://libzmq.readthedocs.io/en/latest/zmq_poll.html) based event loop.
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
the
|
|
153
|
+
When a `(socket, flags) -> handler` mapping context manager is used [ZMQPoll.event_handler][]
|
|
154
|
+
`handler` is called with the socket and event when the event occurs on the socket. The
|
|
155
|
+
callback occurs in the zmq poll thread. [async_kernel.caller.Caller][] is recommended to
|
|
156
|
+
to scheduling code execution in different threads.
|
|
157
|
+
|
|
158
|
+
Only [ZMQPollSocket][] sockets created using the [ZMQPoll.socket][] factory function are
|
|
159
|
+
allowed.
|
|
160
|
+
|
|
161
|
+
The methods [ZMQPoll.execute][] and the async version [ZMQPoll.aexecute][] are provided
|
|
162
|
+
to executed code in the zmq_poll thread, which is useful when creating and configuring
|
|
163
|
+
sockets to reduce thread switching.
|
|
156
164
|
"""
|
|
157
165
|
|
|
158
166
|
stopped: Fixed[Self, ProtectedPending] = Fixed(ProtectedPending)
|
|
167
|
+
"""Set when the poll thread event has stopped."""
|
|
168
|
+
|
|
159
169
|
sockets: Fixed[Self, set[ZMQPollSocket]] = Fixed(set)
|
|
170
|
+
"""The sockets currently registered with this instance."""
|
|
160
171
|
|
|
161
172
|
def __init__(self) -> None:
|
|
162
173
|
|
|
@@ -235,7 +246,7 @@ class ZMQPoll:
|
|
|
235
246
|
sock.recv()
|
|
236
247
|
|
|
237
248
|
def do_execute() -> None:
|
|
238
|
-
"""Execute pending items added by the `execute` and `
|
|
249
|
+
"""Execute pending items added by the `execute` and `aexecute` methods."""
|
|
239
250
|
while execute:
|
|
240
251
|
md = (pen := execute.popleft()).metadata
|
|
241
252
|
try:
|
|
@@ -244,15 +255,16 @@ class ZMQPoll:
|
|
|
244
255
|
pen.set_exception(e)
|
|
245
256
|
del pen
|
|
246
257
|
|
|
247
|
-
send = context.socket(zmq.SocketType.PAIR)
|
|
248
|
-
wake = context.socket(zmq.SocketType.PAIR)
|
|
258
|
+
send: ZMQPollSocket = context.socket(zmq.SocketType.PAIR) # pyright: ignore[reportAssignmentType]
|
|
259
|
+
wake: ZMQPollSocket = context.socket(zmq.SocketType.PAIR) # pyright: ignore[reportAssignmentType]
|
|
249
260
|
addr = "inproc://async_kernel_zmq_poller_wake"
|
|
250
261
|
sockets = None
|
|
251
262
|
handlers[(wake, zmq.POLLIN)] = on_wake
|
|
252
263
|
|
|
253
264
|
with context, wake, send, wake.bind(addr), send.connect(addr):
|
|
265
|
+
k: T_key
|
|
254
266
|
c: tuple[int, Callable] | None
|
|
255
|
-
started.set_result(send)
|
|
267
|
+
started.set_result(send)
|
|
256
268
|
# The main loop polls the handler keys for events in a loop.
|
|
257
269
|
# It will block until an event occurs.
|
|
258
270
|
try:
|
|
@@ -263,9 +275,9 @@ class ZMQPoll:
|
|
|
263
275
|
do_execute()
|
|
264
276
|
continue
|
|
265
277
|
try:
|
|
266
|
-
for k in
|
|
278
|
+
for k in _zmq_poll(sockets, timeout=-1): # pyright: ignore[reportAssignmentType]
|
|
267
279
|
try:
|
|
268
|
-
handlers[k](*k)
|
|
280
|
+
handlers[k](*k)
|
|
269
281
|
except KeyError:
|
|
270
282
|
sockets = None
|
|
271
283
|
except SystemExit:
|
|
@@ -317,6 +329,7 @@ class ZMQPoll:
|
|
|
317
329
|
self.log.debug("ZMQPoll event loop started")
|
|
318
330
|
|
|
319
331
|
def validate_socket(self, sock: ZMQPollSocket | Any) -> ZMQPollSocket:
|
|
332
|
+
"""Check `sock` is correctly registered."""
|
|
320
333
|
if sock not in self.sockets:
|
|
321
334
|
msg = f"Invalid socket detected! {sock=}"
|
|
322
335
|
raise ValueError(msg)
|
|
@@ -334,7 +347,7 @@ class ZMQPoll:
|
|
|
334
347
|
return self.validate_socket(self.execute(self._zmq_context.socket, socket_type))
|
|
335
348
|
|
|
336
349
|
def execute(self, func: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
337
|
-
"""Execute `func` in the thread waiting for the result synchronously."""
|
|
350
|
+
"""Execute `func` in the 'zmq_poll' thread waiting for the result synchronously."""
|
|
338
351
|
if hasattr(self, "thread"):
|
|
339
352
|
if threading.current_thread() is self.thread:
|
|
340
353
|
return func(*args, **kwargs)
|
|
@@ -346,17 +359,17 @@ class ZMQPoll:
|
|
|
346
359
|
finally:
|
|
347
360
|
pen.metadata.clear()
|
|
348
361
|
del pen
|
|
349
|
-
msg = f"Unable to execute {func=} in {self}. Execution is only
|
|
362
|
+
msg = f"Unable to execute {func=} in {self}. Execution is only supported while in context."
|
|
350
363
|
raise RuntimeError(msg)
|
|
351
364
|
|
|
352
|
-
async def
|
|
353
|
-
"""Execute `func` in the thread waiting for the result asynchronously."""
|
|
365
|
+
async def aexecute(self, func: Callable[P, T], /, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
366
|
+
"""Execute `func` in the 'zmq_poll' thread waiting for the result asynchronously."""
|
|
354
367
|
if hasattr(self, "thread"):
|
|
355
368
|
self._execute.append(pen := Pending[T](func=func, args=args, kwargs=kwargs))
|
|
356
369
|
if not self.stopped.done():
|
|
357
370
|
self._wake()
|
|
358
371
|
return await pen
|
|
359
|
-
msg = f"Unable to execute {func=} in {self}. Execution is only
|
|
372
|
+
msg = f"Unable to execute {func=} in {self}. Execution is only supported while in context."
|
|
360
373
|
raise RuntimeError(msg)
|
|
361
374
|
|
|
362
375
|
@contextmanager
|
|
@@ -370,14 +383,12 @@ class ZMQPoll:
|
|
|
370
383
|
count: tuple[int, Callable[[], Any]] | None = None,
|
|
371
384
|
canceller: Callable[[], Any] | NoValue | None = NoValue,
|
|
372
385
|
) -> Generator[None, Any, None]:
|
|
373
|
-
"""A context manager where `handler` is called with the event number when it occurs for `sock`.
|
|
386
|
+
"""A context manager where `handler` is called in the 'zmq_poll' thread with the event number when it occurs for `sock`.
|
|
374
387
|
|
|
375
|
-
Only one `handler` is allowed per `(socket, flags)` combination.
|
|
376
|
-
inside a caller managed task (call_soon, etc) and the ZMQPoll is stopped, the associated pending
|
|
377
|
-
will be cancelled.
|
|
388
|
+
Only one `handler` is allowed per `(socket, flags)` combination.
|
|
378
389
|
|
|
379
390
|
Args:
|
|
380
|
-
sock: A
|
|
391
|
+
sock: A registered [ZMQPollSocket][].
|
|
381
392
|
handler: A handler to handle the event. The handler is called inside the
|
|
382
393
|
zmq_poll thread. Thread-safe primitives must be used by the handler such
|
|
383
394
|
as [async_kernel.caller.Caller.call_soon][],[async_kernel.caller.Caller.queue_call][], etc.
|
|
@@ -388,12 +399,16 @@ class ZMQPoll:
|
|
|
388
399
|
The callback could be an `event.set` to release the context.
|
|
389
400
|
canceller: A callback to use on the event the poll is stopped. The default cancellation
|
|
390
401
|
behavior is to cancel the pending returned by [async_kernel.caller.Caller.current_pending][].
|
|
391
|
-
Set to None to disable cancellation support. This is
|
|
392
|
-
the context of this
|
|
402
|
+
Set to `None` to disable cancellation support. This is safe when this context manager is
|
|
403
|
+
inside the context of this instance.
|
|
404
|
+
|
|
405
|
+
Raises:
|
|
406
|
+
BusyResourceError: If the `(sock, flags)` combination is already in use.
|
|
407
|
+
RuntimeError: If the default canceller can not be created.
|
|
393
408
|
|
|
394
409
|
Tip:
|
|
395
|
-
The
|
|
396
|
-
|
|
410
|
+
The zmq_poll thread normally disables debugging in the zmq_poll thread so inserting breakpoints
|
|
411
|
+
in the event handler may interfere with debugging.
|
|
397
412
|
"""
|
|
398
413
|
assert not self.stopped.done()
|
|
399
414
|
if canceller is NoValue:
|
|
@@ -184,9 +184,6 @@ class Interface(StartStopTask, Application, Generic[T_shell_co]):
|
|
|
184
184
|
_connections: tuple[Connection[Self], ...] = ()
|
|
185
185
|
"""The connections to the interface for messaging."""
|
|
186
186
|
|
|
187
|
-
iopub_send_first_connection_only = traitlets.Bool(False).tag(config=True)
|
|
188
|
-
"""Send iopub messages on the first connection only."""
|
|
189
|
-
|
|
190
187
|
_connections_lock = Fixed(BinarySemaphore)
|
|
191
188
|
|
|
192
189
|
shell: Fixed[Self, T_shell_co] = Fixed(lambda c: c["owner"].kernel.main_shell)
|
|
@@ -226,10 +223,6 @@ class Interface(StartStopTask, Application, Generic[T_shell_co]):
|
|
|
226
223
|
|
|
227
224
|
return IPShell
|
|
228
225
|
|
|
229
|
-
@traitlets.default("iopub_send_first_connection_only")
|
|
230
|
-
def _default_iopub_send_first_connection_only(self) -> bool:
|
|
231
|
-
return sys.platform == "emscripten"
|
|
232
|
-
|
|
233
226
|
@classmethod
|
|
234
227
|
@override
|
|
235
228
|
def initialized(cls) -> bool:
|
|
@@ -490,8 +483,6 @@ class Interface(StartStopTask, Application, Generic[T_shell_co]):
|
|
|
490
483
|
for c in self._connections:
|
|
491
484
|
try:
|
|
492
485
|
c.iopub_send(msg_type, content, metadata=metadata, parent=parent, ident=ident, buffers=buffers)
|
|
493
|
-
if self.iopub_send_first_connection_only:
|
|
494
|
-
break
|
|
495
486
|
except Exception as e:
|
|
496
487
|
self.log.exception("iopub_send failed for connection %r", c, exc_info=e)
|
|
497
488
|
|
|
@@ -79,38 +79,39 @@ def create_interface_messge_callback_handler(
|
|
|
79
79
|
pack_unpack: tuple[Callable[[Message], T], Callable[[T], Message]] = (pack_json_str, unpack_json),
|
|
80
80
|
) -> Callable[[T, BuffersType], None]:
|
|
81
81
|
""
|
|
82
|
-
|
|
82
|
+
conn: Connection | None = None
|
|
83
83
|
lock = BinarySemaphore()
|
|
84
84
|
session_calls = set()
|
|
85
85
|
pack, unpack = pack_unpack
|
|
86
86
|
|
|
87
87
|
def handle_msg(packed_msg: T, buffers: BuffersType | None = None) -> None:
|
|
88
88
|
"""Handle a packed message."""
|
|
89
|
+
nonlocal conn
|
|
89
90
|
msg: Message = unpack(packed_msg)
|
|
90
91
|
msg["buffers"] = [] if buffers is None else buffers
|
|
91
92
|
session: str = msg["header"]["session"]
|
|
92
93
|
session_calls.add(session)
|
|
93
|
-
conn: Connection | None
|
|
94
94
|
|
|
95
|
-
if
|
|
95
|
+
if conn is None:
|
|
96
96
|
with lock:
|
|
97
|
-
if
|
|
97
|
+
if conn is None:
|
|
98
98
|
conn = Connection(interface.caller, session_id=session)
|
|
99
99
|
|
|
100
|
-
def transmit_msg(msg: Message, ident: list[bytes]) -> None:
|
|
100
|
+
def transmit_msg(msg: Message, ident: list[bytes], conn=conn) -> None:
|
|
101
101
|
"""Pack and send a message."""
|
|
102
|
-
#
|
|
102
|
+
# Jupyterlite
|
|
103
|
+
if parent := msg["parent_header"]:
|
|
104
|
+
msg["header"]["session"] = parent["session"]
|
|
105
|
+
|
|
103
106
|
buffers: BuffersType = msg.pop("buffers") # pyright: ignore[reportAssignmentType]
|
|
104
107
|
reply = send(pack(msg), buffers, blocking_reply := msg["channel"] == Channel.stdin)
|
|
105
108
|
if blocking_reply:
|
|
106
|
-
conn.handle_incoming_msg(unpack(reply), [
|
|
109
|
+
conn.handle_incoming_msg(unpack(reply), [])
|
|
107
110
|
|
|
108
111
|
conn.transmit_msg = transmit_msg
|
|
109
112
|
conn.stopped.add_done_callback(lambda _: delattr(conn, "transmit_msg"))
|
|
110
113
|
conn.start()
|
|
111
|
-
conn.stopping.add_done_callback(lambda _: cache.pop(session))
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
conn.handle_incoming_msg(msg, [conn.bsession])
|
|
115
|
+
conn.handle_incoming_msg(msg, [])
|
|
115
116
|
|
|
116
117
|
return handle_msg
|
|
@@ -74,9 +74,6 @@ class BaseMessage(StartStopTask, LoggingConfigurable, MessageProtocol):
|
|
|
74
74
|
session_id: Fixed[Self, str] = Fixed(lambda c: c["owner"]._session_id)
|
|
75
75
|
"""Used to identify this object as the `session` in a message header."""
|
|
76
76
|
|
|
77
|
-
bsession: Fixed[Self, bytes] = Fixed[Self, bytes](lambda c: c["owner"].session_id.encode())
|
|
78
|
-
"""Used to identfiy this object as the origin of a message."""
|
|
79
|
-
|
|
80
77
|
_pending_messages: Fixed[Self, dict[str, PendingMessage[Any]]] = Fixed(dict)
|
|
81
78
|
"""A mapping of the `msg_id` of message requests to the pending that is resolved with a reply."""
|
|
82
79
|
|
|
@@ -201,6 +198,13 @@ class Connection(HasInterface[T_interface_co], BaseMessage, Generic[T_interface_
|
|
|
201
198
|
|
|
202
199
|
@override
|
|
203
200
|
def handle_incoming_msg(self, msg: Message, ident: list[bytes]) -> None:
|
|
201
|
+
"""The handler for messages received on this connection.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
msg: A new message.
|
|
205
|
+
ident: A list of bytes to route a reply message back to the origin. This can
|
|
206
|
+
be an empty list when there is only one connection, such as [LocalClient][].
|
|
207
|
+
"""
|
|
204
208
|
if msg["header"]["msg_type"].endswith("_reply"):
|
|
205
209
|
self.handle_reply(msg)
|
|
206
210
|
else:
|
|
@@ -211,7 +211,7 @@ class ZMQConnection(ZMQMessage, Connection[T_interface_co], Generic[T_interface_
|
|
|
211
211
|
self._sockets[channel] = socket
|
|
212
212
|
socket.bind(addr)
|
|
213
213
|
|
|
214
|
-
await self.zmq_poll.
|
|
214
|
+
await self.zmq_poll.aexecute(bind_sockets)
|
|
215
215
|
|
|
216
216
|
@override
|
|
217
217
|
def connection_info(self) -> str:
|
|
@@ -277,7 +277,7 @@ class ZMQClient(BaseClient[T_interface_co], ZMQMessage, Generic[T_interface_co])
|
|
|
277
277
|
self._sockets[channel] = socket
|
|
278
278
|
return socket
|
|
279
279
|
|
|
280
|
-
return await self.zmq_poll.
|
|
280
|
+
return await self.zmq_poll.aexecute(open_socket)
|
|
281
281
|
|
|
282
282
|
async def _establish_connection(self, timeout: float | None) -> None:
|
|
283
283
|
# Wait for welcome
|
|
@@ -35,9 +35,7 @@ async def test_start_kernel_callable_interface(anyio_backend: Backend):
|
|
|
35
35
|
messages.append(msg)
|
|
36
36
|
return None
|
|
37
37
|
|
|
38
|
-
callable_interface = await start_kernel_callable_interface(
|
|
39
|
-
send=from_interface, stopped=stopped.set, settings={"Interface.iopub_send_first_connection_only": True}
|
|
40
|
-
)
|
|
38
|
+
callable_interface = await start_kernel_callable_interface(send=from_interface, stopped=stopped.set)
|
|
41
39
|
interface = Interface.instance()
|
|
42
40
|
async with LocalClient().start() as client:
|
|
43
41
|
await client.kernel_info()
|
|
@@ -158,15 +158,15 @@ class Test_zmq_Poll:
|
|
|
158
158
|
|
|
159
159
|
async def test_poll_execute_states(self, caller: Caller):
|
|
160
160
|
zmq_poll = ZMQPoll()
|
|
161
|
-
match = "
|
|
161
|
+
match = "Unable to execute"
|
|
162
162
|
# Pre-running
|
|
163
163
|
with pytest.raises(RuntimeError, match=match):
|
|
164
164
|
zmq_poll.execute(lambda: 1 + 1)
|
|
165
165
|
with pytest.raises(RuntimeError, match=match):
|
|
166
|
-
await zmq_poll.
|
|
166
|
+
await zmq_poll.aexecute(lambda: 1 + 1)
|
|
167
167
|
# Running
|
|
168
168
|
with zmq_poll:
|
|
169
|
-
assert (await zmq_poll.
|
|
169
|
+
assert (await zmq_poll.aexecute(lambda: 1 + 1)) == 2
|
|
170
170
|
assert (zmq_poll.execute(lambda: 1 + 1)) == 2
|
|
171
171
|
with pytest.raises(RuntimeError, match="stopped"), zmq_poll:
|
|
172
172
|
None # noqa: B018 # pyright: ignore[reportUnusedExpression]
|
|
@@ -174,11 +174,11 @@ class Test_zmq_Poll:
|
|
|
174
174
|
with pytest.raises(RuntimeError, match=match):
|
|
175
175
|
assert zmq_poll.execute(threading.current_thread) is zmq_poll.thread
|
|
176
176
|
with pytest.raises(RuntimeError, match=match):
|
|
177
|
-
assert await zmq_poll.
|
|
177
|
+
assert await zmq_poll.aexecute(threading.current_thread) is zmq_poll.thread
|
|
178
178
|
|
|
179
179
|
async def test_poll_socket_states(self, caller: Caller):
|
|
180
180
|
zmq_poll = ZMQPoll()
|
|
181
|
-
match = "
|
|
181
|
+
match = "Unable to execute"
|
|
182
182
|
# Pre-running
|
|
183
183
|
with pytest.raises(RuntimeError, match=match):
|
|
184
184
|
zmq_poll.socket(zmq.SocketType.DEALER)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|