async-kernel 0.20.2__tar.gz → 0.21.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.20.2 → async_kernel-0.21.0}/.github/workflows/ci.yml +6 -6
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/workflows/new_release.yml +1 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/workflows/publish-docs.yml +1 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/workflows/publish-to-pypi.yml +1 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.pre-commit-config.yaml +4 -4
- {async_kernel-0.20.2 → async_kernel-0.21.0}/CHANGELOG.md +36 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/PKG-INFO +1 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/_version.py +2 -2
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/event_loop.md +2 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/pyproject.toml +2 -2
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/caller.py +114 -112
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/command.py +3 -3
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/common.py +0 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/debugger.py +12 -23
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/event_loop/run.py +11 -7
- async_kernel-0.21.0/src/async_kernel/event_loop/zmq_poll.py +228 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/interface/base.py +45 -41
- async_kernel-0.21.0/src/async_kernel/interface/zmq.py +282 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/kernel.py +17 -81
- async_kernel-0.21.0/src/async_kernel/outstream.py +102 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/pending.py +25 -8
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/shell/ipshell.py +2 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/typing.py +9 -7
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/utils.py +2 -2
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/conftest.py +29 -30
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_base_interface.py +4 -2
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_callable_interface.py +4 -3
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_caller.py +44 -35
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_command.py +19 -12
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_event_loop.py +7 -0
- async_kernel-0.21.0/tests/test_eventloop_zmq_poll.py +137 -0
- async_kernel-0.20.2/tests/test_iostream.py → async_kernel-0.21.0/tests/test_outstream.py +1 -1
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_zmq_interface.py +47 -43
- async_kernel-0.21.0/tests/test_zmq_interface_ip.py +61 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/utils.py +2 -2
- {async_kernel-0.20.2 → async_kernel-0.21.0}/uv.lock +554 -557
- async_kernel-0.20.2/src/async_kernel/interface/zmq.py +0 -346
- async_kernel-0.20.2/tests/test_zmq_interface_ip.py +0 -31
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/dependabot.yaml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/release.yml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/workflows/enforce-label.yml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.github/workflows/pre-commit.yml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.gitignore +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.vscode/launch.json +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.vscode/settings.json +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/.vscode/spellright.dict +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/CONTRIBUTING.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/IPYTHON_LICENSE +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/LICENSE +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/README.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/cliff.toml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/about/changelog.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/about/contributing.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/about/index.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/about/license.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/index.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/javascripts/extra.js +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/notebooks/caller.ipynb +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/notebooks/concurrency.ipynb +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/notebooks/custom_kernel.ipynb +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/overrides/main.html +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/caller.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/comm.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/command.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/common.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/debugger.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/index.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/interface.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/ipshell.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/kernel.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/kernelspec.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/pending.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/shell.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/typing.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/reference/utils.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/stylesheets/extra.css +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/thread_safety.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/usage/commands.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/docs/usage/index.md +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/hatch_build.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/mkdocs.yml +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/__init__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/__main__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/comm.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/compat/attr_docs.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/compat/json.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/compiler.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/event_loop/__init__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/event_loop/qt_host.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/event_loop/tk_host.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/interface/__init__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/interface/callable.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/interface/ip_app.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/kernelspec.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/py.typed +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/resources/logo-32x32.png +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/resources/logo-64x64.png +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/resources/logo-svg.svg +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/shell/__init__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/src/async_kernel/shell/base.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/__init__.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/references.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_comm.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_common.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_compat.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_debugger.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_debugger_static.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_encryption.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_enter_kernel.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_kernel_ipshell.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_kernelspec.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_message_spec.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_pending.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_subclass.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_typing.py +0 -0
- {async_kernel-0.20.2 → async_kernel-0.21.0}/tests/test_utils.py +0 -0
|
@@ -20,12 +20,12 @@ jobs:
|
|
|
20
20
|
fetch-depth: 0
|
|
21
21
|
|
|
22
22
|
- name: Install uv
|
|
23
|
-
uses: astral-sh/setup-uv@
|
|
23
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
24
24
|
|
|
25
25
|
- name: Type checking with basedpyright
|
|
26
26
|
run: |
|
|
27
27
|
uv sync
|
|
28
|
-
uvx basedpyright@1.39.
|
|
28
|
+
uvx basedpyright@1.39.9
|
|
29
29
|
|
|
30
30
|
test:
|
|
31
31
|
needs: type-checking
|
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
uses: actions/checkout@v7
|
|
46
46
|
|
|
47
47
|
- name: Install uv
|
|
48
|
-
uses: astral-sh/setup-uv@
|
|
48
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
49
49
|
with:
|
|
50
50
|
version-file: "pyproject.toml"
|
|
51
51
|
python-version: ${{ matrix.python-version }}
|
|
@@ -71,7 +71,7 @@ jobs:
|
|
|
71
71
|
if: ${{runner.debug == '1'}}
|
|
72
72
|
timeout-minutes: 3
|
|
73
73
|
run: |
|
|
74
|
-
uv run pytest -vvl --override-ini=log_cli_level=DEBUG
|
|
74
|
+
uv run pytest -vvl --override-ini=log_cli_level=DEBUG
|
|
75
75
|
|
|
76
76
|
- name: Run tests
|
|
77
77
|
if: ${{runner.debug != '1'}}
|
|
@@ -87,7 +87,7 @@ jobs:
|
|
|
87
87
|
fetch-depth: 0
|
|
88
88
|
|
|
89
89
|
- name: Install uv
|
|
90
|
-
uses: astral-sh/setup-uv@
|
|
90
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
91
91
|
with:
|
|
92
92
|
version-file: "pyproject.toml"
|
|
93
93
|
python-version: "3.13"
|
|
@@ -116,7 +116,7 @@ jobs:
|
|
|
116
116
|
fetch-depth: 0
|
|
117
117
|
|
|
118
118
|
- name: Install uv
|
|
119
|
-
uses: astral-sh/setup-uv@
|
|
119
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
120
120
|
with:
|
|
121
121
|
version-file: "pyproject.toml"
|
|
122
122
|
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
fetch-depth: 0
|
|
32
32
|
ref: main
|
|
33
33
|
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@
|
|
34
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
35
35
|
with:
|
|
36
36
|
version-file: "pyproject.toml"
|
|
37
37
|
|
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
|
28
28
|
|
|
29
29
|
- name: Install uv
|
|
30
|
-
uses: astral-sh/setup-uv@
|
|
30
|
+
uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
31
31
|
with:
|
|
32
32
|
version-file: "pyproject.toml"
|
|
33
33
|
|
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
# Fetch full history for setuptools-scm
|
|
32
32
|
fetch-depth: 0
|
|
33
33
|
ref: main
|
|
34
|
-
- uses: astral-sh/setup-uv@
|
|
34
|
+
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
|
|
35
35
|
with:
|
|
36
36
|
version-file: "pyproject.toml"
|
|
37
37
|
|
|
@@ -28,12 +28,12 @@ repos:
|
|
|
28
28
|
- id: check-json5
|
|
29
29
|
|
|
30
30
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
|
31
|
-
rev: 0.37.
|
|
31
|
+
rev: 0.37.4
|
|
32
32
|
hooks:
|
|
33
33
|
- id: check-github-workflows
|
|
34
34
|
|
|
35
35
|
- repo: https://github.com/rbubley/mirrors-prettier
|
|
36
|
-
rev: v3.
|
|
36
|
+
rev: v3.9.5
|
|
37
37
|
hooks:
|
|
38
38
|
- id: prettier
|
|
39
39
|
types_or: [yaml, html, json]
|
|
@@ -48,7 +48,7 @@ repos:
|
|
|
48
48
|
additional_dependencies: [black==23.7.0]
|
|
49
49
|
|
|
50
50
|
- repo: https://github.com/codespell-project/codespell
|
|
51
|
-
rev: v2.4.
|
|
51
|
+
rev: v2.4.3
|
|
52
52
|
hooks:
|
|
53
53
|
- id: codespell
|
|
54
54
|
args: ["-L", "sur,nd"]
|
|
@@ -59,7 +59,7 @@ repos:
|
|
|
59
59
|
- id: python-use-type-annotations
|
|
60
60
|
|
|
61
61
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
62
|
-
rev: v0.15.
|
|
62
|
+
rev: v0.15.22
|
|
63
63
|
hooks:
|
|
64
64
|
- id: ruff-check
|
|
65
65
|
types_or: [python, jupyter]
|
|
@@ -5,7 +5,39 @@ 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.
|
|
8
|
+
## [0.21.0] - 2026-07-18
|
|
9
|
+
|
|
10
|
+
### <!-- 1 --> 🚀 Features
|
|
11
|
+
|
|
12
|
+
- Add print_concat to concatenate print statements into a single string when 'file' is not specified. [#522](https://github.com/fleming79/async-kernel/pull/522)
|
|
13
|
+
|
|
14
|
+
- Added PollZMQ - a thread based zmq poller callback registry. [#515](https://github.com/fleming79/async-kernel/pull/515)
|
|
15
|
+
|
|
16
|
+
- Support loop_factory as an importable path. [#514](https://github.com/fleming79/async-kernel/pull/514)
|
|
17
|
+
|
|
18
|
+
### <!-- 6 --> 🌀 Miscellaneous
|
|
19
|
+
|
|
20
|
+
- Poll maintenance [#529](https://github.com/fleming79/async-kernel/pull/529)
|
|
21
|
+
|
|
22
|
+
- Add threadsafe option to Poll.socket [#528](https://github.com/fleming79/async-kernel/pull/528)
|
|
23
|
+
|
|
24
|
+
- Update lock files and pre-commit and basedpyright. [#527](https://github.com/fleming79/async-kernel/pull/527)
|
|
25
|
+
|
|
26
|
+
- Kernel updates - handle external kernel interrupt. [#526](https://github.com/fleming79/async-kernel/pull/526)
|
|
27
|
+
|
|
28
|
+
- Add docstrings for Poll [#525](https://github.com/fleming79/async-kernel/pull/525)
|
|
29
|
+
|
|
30
|
+
- Bump astral-sh/setup-uv from 8.2.0 to 8.3.0 in the actions group [#518](https://github.com/fleming79/async-kernel/pull/518)
|
|
31
|
+
|
|
32
|
+
- Change PollZMQ to Poll and move to event_loop/zmq_poll.py. [#524](https://github.com/fleming79/async-kernel/pull/524)
|
|
33
|
+
|
|
34
|
+
- Add more log messages when running pytest from the command line. [#523](https://github.com/fleming79/async-kernel/pull/523)
|
|
35
|
+
|
|
36
|
+
- Move OutStream from kernel.py to outstream.py [#521](https://github.com/fleming79/async-kernel/pull/521)
|
|
37
|
+
|
|
38
|
+
- Refactor Caller. [#520](https://github.com/fleming79/async-kernel/pull/520)
|
|
39
|
+
|
|
40
|
+
## [0.20.2] - 2026-06-30
|
|
9
41
|
|
|
10
42
|
### <!-- 5 --> 📝 Documentation
|
|
11
43
|
|
|
@@ -13,6 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
45
|
|
|
14
46
|
### <!-- 6 --> 🌀 Miscellaneous
|
|
15
47
|
|
|
48
|
+
- Prepare for release v0.20.2 [#512](https://github.com/fleming79/async-kernel/pull/512)
|
|
49
|
+
|
|
16
50
|
- Avoid attribute error in Comm.publish_msg in case the parent is gc before the object. [#511](https://github.com/fleming79/async-kernel/pull/511)
|
|
17
51
|
|
|
18
52
|
- Update uv.lock and pre-commit-config.yaml [#510](https://github.com/fleming79/async-kernel/pull/510)
|
|
@@ -1419,6 +1453,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1419
1453
|
|
|
1420
1454
|
- Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
|
|
1421
1455
|
|
|
1456
|
+
[0.21.0]: https://github.com/fleming79/async-kernel/compare/v0.20.2..v0.21.0
|
|
1422
1457
|
[0.20.2]: https://github.com/fleming79/async-kernel/compare/v0.20.1..v0.20.2
|
|
1423
1458
|
[0.20.1]: https://github.com/fleming79/async-kernel/compare/v0.20.0..v0.20.1
|
|
1424
1459
|
[0.20.0]: https://github.com/fleming79/async-kernel/compare/v0.19.2..v0.20.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-kernel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.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
|
|
@@ -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
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.21.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 21, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -85,7 +85,7 @@ test = [
|
|
|
85
85
|
"trio>=0.31.0",
|
|
86
86
|
]
|
|
87
87
|
uvloop = ["uvloop; python_version <= '3.13' and sys_platform != 'win32' and implementation_name == 'cpython'",
|
|
88
|
-
"winloop; sys_platform == 'win32' and implementation_name == 'cpython'"
|
|
88
|
+
"winloop; sys_platform == 'win32' and implementation_name == 'cpython' and python_version <= '3.13'"
|
|
89
89
|
]
|
|
90
90
|
dev = [
|
|
91
91
|
"matplotlib>=3.10.9; python_version < '3.15'",
|
|
@@ -248,7 +248,7 @@ reportUnusedParameter = false
|
|
|
248
248
|
reportUnknownLambdaType = false
|
|
249
249
|
|
|
250
250
|
[tool.uv]
|
|
251
|
-
required-version = "==0.11.
|
|
251
|
+
required-version = "==0.11.29"
|
|
252
252
|
|
|
253
253
|
[tool.uv.sources]
|
|
254
254
|
jupyter-client = { git = "https://github.com/jupyter/jupyter_client.git" }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import atexit
|
|
4
5
|
import contextlib
|
|
5
6
|
import contextvars
|
|
6
7
|
import inspect
|
|
@@ -18,12 +19,13 @@ from types import CoroutineType
|
|
|
18
19
|
from typing import TYPE_CHECKING, Any, ClassVar, Literal, Self, Unpack, final
|
|
19
20
|
|
|
20
21
|
import anyio
|
|
21
|
-
from aiologic import BinarySemaphore,
|
|
22
|
+
from aiologic import BinarySemaphore, CountdownEvent
|
|
22
23
|
from aiologic.lowlevel import create_async_event, current_async_library
|
|
23
24
|
from aiologic.meta import await_for, iscoroutinelike
|
|
24
25
|
from typing_extensions import override
|
|
25
26
|
from wrapt import lazy_import
|
|
26
27
|
|
|
28
|
+
import async_kernel.event_loop
|
|
27
29
|
from async_kernel import utils
|
|
28
30
|
from async_kernel.common import Fixed, KernelInterrupt, SingleAsyncQueue
|
|
29
31
|
from async_kernel.event_loop.run import Host, get_start_guest_run
|
|
@@ -41,8 +43,6 @@ if TYPE_CHECKING:
|
|
|
41
43
|
from types import CoroutineType
|
|
42
44
|
|
|
43
45
|
import trio # noqa: TC004
|
|
44
|
-
import zmq
|
|
45
|
-
from aiologic.lowlevel import AsyncEvent
|
|
46
46
|
|
|
47
47
|
from async_kernel.typing import P
|
|
48
48
|
|
|
@@ -195,12 +195,9 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
195
195
|
CallerState.stopping: "🏁 stopping",
|
|
196
196
|
CallerState.stopped: "🏁 stopped",
|
|
197
197
|
}
|
|
198
|
-
_zmq_context: zmq.Context[Any] | None = None
|
|
199
|
-
|
|
200
|
-
_parent_ref: weakref.ref[Self] | None = None
|
|
201
198
|
|
|
202
199
|
# Fixed
|
|
203
|
-
|
|
200
|
+
_inst_lock = Fixed(BinarySemaphore)
|
|
204
201
|
_children: Fixed[Self, set[Self]] = Fixed(set)
|
|
205
202
|
_tasks: Fixed[Self, set[asyncio.Task]] = Fixed(set)
|
|
206
203
|
_worker_pool: Fixed[Self, deque[Self]] = Fixed(deque)
|
|
@@ -209,21 +206,18 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
209
206
|
lambda c: SingleAsyncQueue(reject=c["owner"]._reject)
|
|
210
207
|
)
|
|
211
208
|
_guest_queues: Fixed[Self, dict[Backend, SingleAsyncQueue[Pending | tuple[Callable, tuple, dict]]]] = Fixed(dict)
|
|
212
|
-
|
|
209
|
+
_guest_done_event: Fixed[Any, CountdownEvent] = Fixed(CountdownEvent)
|
|
213
210
|
_stopping = Fixed(Pending[None])
|
|
214
211
|
"A pending that is set done the first time stop is called."
|
|
215
212
|
|
|
216
|
-
stopped = Fixed(
|
|
217
|
-
"
|
|
213
|
+
stopped = Fixed(Pending)
|
|
214
|
+
"A pending that is done when the caller is stopped."
|
|
218
215
|
|
|
216
|
+
_children_countdown: CountdownEvent | None = None
|
|
219
217
|
_pending_var: contextvars.ContextVar[Pending | None] = contextvars.ContextVar("_pending_var", default=None)
|
|
220
218
|
|
|
221
219
|
log: logging.Logger | logging.LoggerAdapter
|
|
222
220
|
""
|
|
223
|
-
iopub_sockets: ClassVar[dict[int, zmq.Socket]] = {}
|
|
224
|
-
""
|
|
225
|
-
iopub_url: ClassVar = "inproc://iopub"
|
|
226
|
-
""
|
|
227
221
|
|
|
228
222
|
@property
|
|
229
223
|
def name(self) -> str:
|
|
@@ -260,11 +254,6 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
260
254
|
"Returns `True` if the caller is protected from stopping."
|
|
261
255
|
return self._protected
|
|
262
256
|
|
|
263
|
-
@property
|
|
264
|
-
def zmq_context(self) -> zmq.Context | None:
|
|
265
|
-
"A zmq socket, which if present indicates that an iopub socket is loaded."
|
|
266
|
-
return self._zmq_context
|
|
267
|
-
|
|
268
257
|
@property
|
|
269
258
|
def running(self) -> bool:
|
|
270
259
|
"Returns `True` when the caller is available to run requests."
|
|
@@ -288,8 +277,9 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
288
277
|
@property
|
|
289
278
|
def parent(self) -> Self | None:
|
|
290
279
|
"The parent caller if it exists."
|
|
291
|
-
|
|
292
|
-
|
|
280
|
+
return self._parent_ref()
|
|
281
|
+
|
|
282
|
+
def _parent_ref(self) -> Self | None:
|
|
293
283
|
return None
|
|
294
284
|
|
|
295
285
|
def _get_info(self) -> dict[str, Any]:
|
|
@@ -334,7 +324,6 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
334
324
|
- backend: The async backend to use.
|
|
335
325
|
- backend_options: Options for the backend.
|
|
336
326
|
- protected: Whether the Caller is protected.
|
|
337
|
-
- zmq_context: ZeroMQ context.
|
|
338
327
|
- log: Logger instance.
|
|
339
328
|
|
|
340
329
|
Returns:
|
|
@@ -377,7 +366,6 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
377
366
|
inst._backend_options = kwargs.get("backend_options")
|
|
378
367
|
inst._host_options = kwargs.get("host_options")
|
|
379
368
|
inst._protected = kwargs.get("protected", False)
|
|
380
|
-
inst._zmq_context = kwargs.get("zmq_context")
|
|
381
369
|
inst.log = kwargs.get("log") or logging.LoggerAdapter(logging.getLogger())
|
|
382
370
|
if (sys.platform == "emscripten") and (caller_id is None):
|
|
383
371
|
caller_id = id(inst)
|
|
@@ -390,6 +378,8 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
390
378
|
inst.start_sync(no_debug=kwargs.get("no_debug", False))
|
|
391
379
|
assert inst._caller_id
|
|
392
380
|
assert inst._caller_id not in cls._instances
|
|
381
|
+
atexit.register(inst.stop, force=True)
|
|
382
|
+
inst._stopping.add_done_callback(lambda _: atexit.unregister(inst.stop))
|
|
393
383
|
cls._instances[inst._caller_id] = inst
|
|
394
384
|
return inst
|
|
395
385
|
|
|
@@ -405,6 +395,10 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
405
395
|
self._state = CallerState.start_sync
|
|
406
396
|
|
|
407
397
|
async def run_caller_in_context() -> None:
|
|
398
|
+
if self._state in [CallerState.stopping, CallerState.stopped]:
|
|
399
|
+
return
|
|
400
|
+
if not self._name:
|
|
401
|
+
self._name = self._thread.name
|
|
408
402
|
if self._state is CallerState.start_sync:
|
|
409
403
|
self._state = CallerState.initial
|
|
410
404
|
if no_debug:
|
|
@@ -432,69 +426,74 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
432
426
|
if not "shutdown" not in str(e):
|
|
433
427
|
raise
|
|
434
428
|
|
|
435
|
-
threading.Thread(target=to_thread
|
|
429
|
+
threading.Thread(target=to_thread).start()
|
|
436
430
|
else:
|
|
437
431
|
|
|
438
432
|
def async_kernel_caller() -> None:
|
|
439
433
|
self._thread, self._caller_id = threading.current_thread(), threading.get_ident()
|
|
434
|
+
settings = RunSettings(
|
|
435
|
+
backend=self.backend,
|
|
436
|
+
host=self.host,
|
|
437
|
+
backend_options=self.backend_options,
|
|
438
|
+
host_options=self.host_options,
|
|
439
|
+
)
|
|
440
440
|
try:
|
|
441
|
-
|
|
442
|
-
# No gui (default)
|
|
443
|
-
anyio.run(run_caller_in_context, backend=self.backend, backend_options=self.backend_options)
|
|
444
|
-
else:
|
|
445
|
-
# A gui with the backend running as a guest.
|
|
446
|
-
settings = RunSettings(
|
|
447
|
-
backend=self.backend,
|
|
448
|
-
host=self.host,
|
|
449
|
-
backend_options=self.backend_options,
|
|
450
|
-
host_options=self.host_options,
|
|
451
|
-
)
|
|
452
|
-
Host.run(run_caller_in_context, (), settings)
|
|
441
|
+
async_kernel.event_loop.run(run_caller_in_context, (), settings)
|
|
453
442
|
except Exception as e:
|
|
454
443
|
if not self._stopping.done():
|
|
455
|
-
self.stop()
|
|
444
|
+
self.stop(force=True)
|
|
456
445
|
self.log.exception("%s exited early", self, exc_info=e)
|
|
457
446
|
raise
|
|
458
447
|
|
|
459
|
-
thread = threading.Thread(target=async_kernel_caller, name=self.
|
|
448
|
+
thread = threading.Thread(target=async_kernel_caller, name=self._name or None)
|
|
460
449
|
thread.start()
|
|
461
450
|
assert thread.ident
|
|
462
451
|
self._thread, self._caller_id = thread, thread.ident
|
|
463
452
|
|
|
464
|
-
def stop(self, *, force: bool = False) ->
|
|
453
|
+
def stop(self, *, force: bool = False) -> None:
|
|
465
454
|
"""
|
|
466
455
|
Stop the caller cancelling all incomplete tasks.
|
|
467
456
|
|
|
468
457
|
Args:
|
|
469
458
|
force: If the caller is protected the call is a no-op unless force=True.
|
|
459
|
+
Returns:
|
|
460
|
+
Event: If stopping is initiated or complete.
|
|
461
|
+
None: If stop is ignored when the caller is protected.
|
|
470
462
|
"""
|
|
471
|
-
if
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
self.
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
for child in self.children:
|
|
463
|
+
if self._protected and not force:
|
|
464
|
+
self.log.warning("Non-force stop ignored for %s", self)
|
|
465
|
+
return
|
|
466
|
+
with self._inst_lock:
|
|
467
|
+
if self._stopping.done():
|
|
468
|
+
return
|
|
469
|
+
state = self._state
|
|
470
|
+
self._state = CallerState.stopping
|
|
471
|
+
self._stopping.set_result(None)
|
|
472
|
+
self.log.debug("Stopping %s", self)
|
|
473
|
+
# Remove from worker pool
|
|
474
|
+
if (parent := self.parent) and (workers := parent._worker_pool):
|
|
475
|
+
with contextlib.suppress(ValueError):
|
|
476
|
+
workers.remove(self)
|
|
477
|
+
# Invoke stop callbacks
|
|
478
|
+
for child in self._children.copy():
|
|
488
479
|
child.stop(force=True)
|
|
489
|
-
|
|
480
|
+
# Shutdown queue_call
|
|
481
|
+
for func in self._queue_map.copy():
|
|
482
|
+
self.queue_close(func)
|
|
483
|
+
# Stop the children
|
|
484
|
+
if state in [CallerState.initial, CallerState.start_sync]:
|
|
485
|
+
self._stop_finalize()
|
|
490
486
|
|
|
491
487
|
def _stop_finalize(self) -> None:
|
|
492
|
-
self.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
488
|
+
with self._inst_lock:
|
|
489
|
+
if not self.stopped.done():
|
|
490
|
+
if (countdown := self._children_countdown) is not None:
|
|
491
|
+
countdown.wait()
|
|
492
|
+
self._instances.pop(self._caller_id)
|
|
493
|
+
self._queue.stop()
|
|
494
|
+
self._state = CallerState.stopped
|
|
495
|
+
self.stopped.set_result(None)
|
|
496
|
+
self.log.debug("Stopped %s", self)
|
|
498
497
|
|
|
499
498
|
@asynccontextmanager
|
|
500
499
|
async def __asynccontextmanager__(self) -> AsyncGenerator[Self]:
|
|
@@ -505,35 +504,25 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
505
504
|
if self._state is CallerState.stopped:
|
|
506
505
|
msg = f"Stopped: {self}"
|
|
507
506
|
raise RuntimeError(msg)
|
|
508
|
-
socket = None
|
|
509
507
|
try:
|
|
510
|
-
if self._zmq_context:
|
|
511
|
-
socket = self._zmq_context.socket(1) # zmq.SocketType.PUB
|
|
512
|
-
socket.connect(self.iopub_url)
|
|
513
|
-
self.iopub_sockets[self._caller_id] = socket
|
|
514
508
|
async with task_factory() as create_task:
|
|
509
|
+
create_task(contextvars.Context(), self._scheduler, self._queue)
|
|
515
510
|
try:
|
|
516
|
-
create_task(contextvars.Context(), self._scheduler, self._queue)
|
|
517
511
|
with anyio.CancelScope() as scope:
|
|
518
512
|
self._stopping.add_done_callback(lambda _: self.call_direct(scope.cancel, "Stopping"))
|
|
519
513
|
self._state = CallerState.running
|
|
514
|
+
self.log.debug("Started %s", self)
|
|
520
515
|
yield self
|
|
521
516
|
finally:
|
|
522
517
|
self.stop(force=True)
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
try:
|
|
529
|
-
if not (caller := self._children.pop()).stopped:
|
|
518
|
+
if self._guest_done_event.value > 0:
|
|
519
|
+
# Warning: `queue` must not be stopped until guests have been stopped.
|
|
520
|
+
with anyio.CancelScope(shield=True):
|
|
521
|
+
await self._guest_done_event
|
|
522
|
+
if (countdown := self._children_countdown) is not None:
|
|
530
523
|
with anyio.CancelScope(shield=True):
|
|
531
|
-
await
|
|
532
|
-
|
|
533
|
-
pass
|
|
534
|
-
if socket:
|
|
535
|
-
self.iopub_sockets.pop(self._caller_id, None)
|
|
536
|
-
socket.close(linger=0)
|
|
524
|
+
await countdown
|
|
525
|
+
finally:
|
|
537
526
|
self._stop_finalize()
|
|
538
527
|
|
|
539
528
|
async def _scheduler(self, queue: SingleAsyncQueue) -> None:
|
|
@@ -630,7 +619,7 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
630
619
|
if worker._idle_time < cutoff:
|
|
631
620
|
with contextlib.suppress(IndexError):
|
|
632
621
|
caller._worker_pool.remove(worker)
|
|
633
|
-
worker.stop()
|
|
622
|
+
worker.stop(force=True)
|
|
634
623
|
if n:
|
|
635
624
|
_cleanup_workers()
|
|
636
625
|
else:
|
|
@@ -689,29 +678,38 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
689
678
|
- The returned caller is added to `children` and stopped with the caller.
|
|
690
679
|
- If 'backend' or 'zmq_context' are not specified they are copied from the caller.
|
|
691
680
|
"""
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
681
|
+
|
|
682
|
+
with self._inst_lock:
|
|
683
|
+
if self._stopping.done():
|
|
684
|
+
msg = f"Caller is stopping or stopped {self}"
|
|
685
|
+
raise RuntimeError(msg)
|
|
696
686
|
if name := kwargs.get("name"):
|
|
697
|
-
for
|
|
698
|
-
if
|
|
699
|
-
if (backend := kwargs.get("backend")) and
|
|
700
|
-
msg = f"Backend mismatch! {backend=} {
|
|
687
|
+
for child in self._children:
|
|
688
|
+
if child.name == name:
|
|
689
|
+
if (backend := kwargs.get("backend")) and child.backend != backend:
|
|
690
|
+
msg = f"Backend mismatch! {backend=} {child.backend=}"
|
|
701
691
|
raise RuntimeError(msg)
|
|
702
|
-
if (host := kwargs.get("host")) and
|
|
703
|
-
msg = f"Host mismatch! {host=} {
|
|
692
|
+
if (host := kwargs.get("host")) and child.host != host:
|
|
693
|
+
msg = f"Host mismatch! {host=} {child.host=}"
|
|
704
694
|
raise RuntimeError(msg)
|
|
705
|
-
return
|
|
695
|
+
return child
|
|
696
|
+
|
|
706
697
|
if "backend" not in kwargs:
|
|
707
698
|
kwargs["backend"] = self._backend
|
|
708
699
|
kwargs["backend_options"] = self.backend_options
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
700
|
+
child = self.__class__("NewThread", **kwargs)
|
|
701
|
+
child._parent_ref = weakref.ref(self)
|
|
702
|
+
if self._children_countdown is None:
|
|
703
|
+
self._children_countdown = CountdownEvent()
|
|
704
|
+
self._children.add(child)
|
|
705
|
+
self._children_countdown.up()
|
|
706
|
+
|
|
707
|
+
def on_child_stopped(_, children=self._children, done=self._children_countdown.down) -> None:
|
|
708
|
+
children.discard(child)
|
|
709
|
+
done()
|
|
710
|
+
|
|
711
|
+
child.stopped.add_done_callback(on_child_stopped)
|
|
712
|
+
return child
|
|
715
713
|
|
|
716
714
|
def schedule_call(
|
|
717
715
|
self,
|
|
@@ -745,7 +743,7 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
745
743
|
if backend is NoValue or (backend := Backend(backend)) is self.backend:
|
|
746
744
|
queue = self._queue
|
|
747
745
|
elif not (queue := self._guest_queues.get(backend)):
|
|
748
|
-
with self.
|
|
746
|
+
with self._inst_lock:
|
|
749
747
|
if backend is Backend.trio:
|
|
750
748
|
trio.sleep # noqa: B018 # Check trio is available.
|
|
751
749
|
if not (queue := self._guest_queues.get(backend)):
|
|
@@ -753,20 +751,23 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
753
751
|
self._guest_queues[backend] = queue
|
|
754
752
|
self._stopping.add_done_callback(lambda _: queue.stop())
|
|
755
753
|
|
|
754
|
+
def guest_done_callback(value: Any):
|
|
755
|
+
self._guest_done_event.down()
|
|
756
|
+
self._guest_queues.pop(backend)
|
|
757
|
+
|
|
756
758
|
def _start_guest() -> None:
|
|
757
759
|
"Start a guest event loop"
|
|
758
760
|
if self._state is CallerState.running:
|
|
759
|
-
host = Host.current(self.thread)
|
|
760
|
-
done = create_async_event(shield=True)
|
|
761
|
+
host: Host[Any] | None = Host.current(self.thread)
|
|
761
762
|
get_start_guest_run(backend)(
|
|
762
763
|
self._scheduler,
|
|
763
764
|
queue,
|
|
764
|
-
done_callback=
|
|
765
|
+
done_callback=guest_done_callback,
|
|
765
766
|
run_sync_soon_threadsafe=host.run_sync_soon_threadsafe if host else self.call_direct,
|
|
766
767
|
run_sync_soon_not_threadsafe=host.run_sync_soon_not_threadsafe if host else None,
|
|
767
768
|
host_uses_signal_set_wakeup_fd=host.host_uses_signal_set_wakeup_fd if host else True,
|
|
768
769
|
)
|
|
769
|
-
self.
|
|
770
|
+
self._guest_done_event.up()
|
|
770
771
|
|
|
771
772
|
self.call_direct(_start_guest)
|
|
772
773
|
queue.append(pen)
|
|
@@ -896,14 +897,14 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
896
897
|
"""
|
|
897
898
|
|
|
898
899
|
def _to_thread_on_done(_) -> None:
|
|
899
|
-
if not caller.stopped and self.running:
|
|
900
|
-
with self.
|
|
900
|
+
if not caller.stopped.done() and self.running:
|
|
901
|
+
with self._inst_lock:
|
|
901
902
|
if len(self._worker_pool) < self.MAX_IDLE_POOL_INSTANCES:
|
|
902
903
|
caller._idle_time = time.monotonic()
|
|
903
904
|
self._worker_pool.append(caller)
|
|
904
905
|
self._start_idle_worker_cleanup_thead()
|
|
905
906
|
else:
|
|
906
|
-
caller.stop()
|
|
907
|
+
caller.stop(force=True)
|
|
907
908
|
|
|
908
909
|
try:
|
|
909
910
|
caller = self._worker_pool.popleft()
|
|
@@ -950,7 +951,7 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
950
951
|
trackers and **not** PendingGroup.
|
|
951
952
|
"""
|
|
952
953
|
if not (pen_ := self._queue_map.get(key := hash(func))):
|
|
953
|
-
with self.
|
|
954
|
+
with self._inst_lock:
|
|
954
955
|
if not (pen_ := self._queue_map.get(key)):
|
|
955
956
|
queue = SingleAsyncQueue[tuple[Callable, tuple, dict]](reject=self._reject)
|
|
956
957
|
with contextlib.suppress(TypeError):
|
|
@@ -962,9 +963,10 @@ class Caller(anyio.AsyncContextManagerMixin):
|
|
|
962
963
|
try:
|
|
963
964
|
async for item in queue:
|
|
964
965
|
try:
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
966
|
+
result = item[0](*item[1], **item[2])
|
|
967
|
+
if iscoroutinelike(result):
|
|
968
|
+
await result
|
|
969
|
+
del result
|
|
968
970
|
except (anyio.get_cancelled_exc_class(), Exception) as e:
|
|
969
971
|
if pen.cancelled():
|
|
970
972
|
raise
|