async-kernel 0.19.2__tar.gz → 0.20.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.
Files changed (114) hide show
  1. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/ci.yml +25 -8
  2. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/new_release.yml +1 -1
  3. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/publish-docs.yml +1 -1
  4. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/publish-to-pypi.yml +1 -1
  5. {async_kernel-0.19.2 → async_kernel-0.20.0}/.pre-commit-config.yaml +4 -4
  6. {async_kernel-0.19.2 → async_kernel-0.20.0}/.vscode/launch.json +2 -1
  7. {async_kernel-0.19.2 → async_kernel-0.20.0}/CHANGELOG.md +53 -0
  8. {async_kernel-0.19.2 → async_kernel-0.20.0}/PKG-INFO +3 -3
  9. {async_kernel-0.19.2 → async_kernel-0.20.0}/_version.py +2 -2
  10. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/interface.md +1 -0
  11. {async_kernel-0.19.2 → async_kernel-0.20.0}/pyproject.toml +19 -15
  12. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/caller.py +48 -36
  13. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/command.py +1 -1
  14. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/common.py +75 -54
  15. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/debugger.py +35 -21
  16. async_kernel-0.20.0/src/async_kernel/interface/__init__.py +64 -0
  17. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/interface/base.py +65 -37
  18. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/interface/callable.py +5 -6
  19. async_kernel-0.20.0/src/async_kernel/interface/ip_app.py +92 -0
  20. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/interface/zmq.py +93 -196
  21. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/kernel.py +32 -56
  22. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/kernelspec.py +32 -21
  23. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/pending.py +12 -9
  24. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/shell/ipshell.py +22 -21
  25. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/typing.py +15 -2
  26. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/utils.py +2 -2
  27. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/conftest.py +61 -34
  28. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_base_interface.py +12 -0
  29. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_callable_interface.py +6 -26
  30. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_caller.py +24 -9
  31. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_command.py +84 -43
  32. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_common.py +7 -5
  33. async_kernel-0.20.0/tests/test_encryption.py +28 -0
  34. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_enter_kernel.py +2 -2
  35. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_event_loop.py +61 -59
  36. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_kernel_ipshell.py +1 -1
  37. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_kernelspec.py +16 -3
  38. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_pending.py +7 -8
  39. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_subclass.py +4 -6
  40. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_utils.py +1 -1
  41. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_zmq_interface.py +7 -5
  42. async_kernel-0.20.0/tests/test_zmq_interface_ip.py +31 -0
  43. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/utils.py +20 -6
  44. {async_kernel-0.19.2 → async_kernel-0.20.0}/uv.lock +577 -525
  45. async_kernel-0.19.2/src/async_kernel/interface/__init__.py +0 -41
  46. async_kernel-0.19.2/tests/test_zmq_messaging.py +0 -39
  47. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/dependabot.yaml +0 -0
  48. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/release.yml +0 -0
  49. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/enforce-label.yml +0 -0
  50. {async_kernel-0.19.2 → async_kernel-0.20.0}/.github/workflows/pre-commit.yml +0 -0
  51. {async_kernel-0.19.2 → async_kernel-0.20.0}/.gitignore +0 -0
  52. {async_kernel-0.19.2 → async_kernel-0.20.0}/.vscode/settings.json +0 -0
  53. {async_kernel-0.19.2 → async_kernel-0.20.0}/.vscode/spellright.dict +0 -0
  54. {async_kernel-0.19.2 → async_kernel-0.20.0}/CONTRIBUTING.md +0 -0
  55. {async_kernel-0.19.2 → async_kernel-0.20.0}/IPYTHON_LICENSE +0 -0
  56. {async_kernel-0.19.2 → async_kernel-0.20.0}/LICENSE +0 -0
  57. {async_kernel-0.19.2 → async_kernel-0.20.0}/README.md +0 -0
  58. {async_kernel-0.19.2 → async_kernel-0.20.0}/cliff.toml +0 -0
  59. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/about/changelog.md +0 -0
  60. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/about/contributing.md +0 -0
  61. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/about/index.md +0 -0
  62. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/about/license.md +0 -0
  63. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/index.md +0 -0
  64. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/javascripts/extra.js +0 -0
  65. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/notebooks/caller.ipynb +0 -0
  66. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/notebooks/concurrency.ipynb +0 -0
  67. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/notebooks/custom_kernel.ipynb +0 -0
  68. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/overrides/main.html +0 -0
  69. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/caller.md +0 -0
  70. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/comm.md +0 -0
  71. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/command.md +0 -0
  72. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/common.md +0 -0
  73. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/debugger.md +0 -0
  74. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/event_loop.md +0 -0
  75. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/index.md +0 -0
  76. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/ipshell.md +0 -0
  77. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/kernel.md +0 -0
  78. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/kernelspec.md +0 -0
  79. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/pending.md +0 -0
  80. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/shell.md +0 -0
  81. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/typing.md +0 -0
  82. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/reference/utils.md +0 -0
  83. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/stylesheets/extra.css +0 -0
  84. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/thread_safety.md +0 -0
  85. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/usage/commands.md +0 -0
  86. {async_kernel-0.19.2 → async_kernel-0.20.0}/docs/usage/index.md +0 -0
  87. {async_kernel-0.19.2 → async_kernel-0.20.0}/hatch_build.py +0 -0
  88. {async_kernel-0.19.2 → async_kernel-0.20.0}/mkdocs.yml +0 -0
  89. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/__init__.py +0 -0
  90. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/__main__.py +0 -0
  91. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/comm.py +0 -0
  92. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/compat/attr_docs.py +0 -0
  93. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/compat/json.py +0 -0
  94. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/compiler.py +0 -0
  95. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/event_loop/__init__.py +0 -0
  96. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
  97. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/event_loop/qt_host.py +0 -0
  98. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/event_loop/run.py +0 -0
  99. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/event_loop/tk_host.py +0 -0
  100. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/py.typed +0 -0
  101. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/resources/logo-32x32.png +0 -0
  102. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/resources/logo-64x64.png +0 -0
  103. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/resources/logo-svg.svg +0 -0
  104. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/shell/__init__.py +0 -0
  105. {async_kernel-0.19.2 → async_kernel-0.20.0}/src/async_kernel/shell/base.py +0 -0
  106. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/__init__.py +0 -0
  107. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/references.py +0 -0
  108. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_comm.py +0 -0
  109. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_compat.py +0 -0
  110. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_debugger.py +0 -0
  111. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_debugger_static.py +0 -0
  112. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_iostream.py +0 -0
  113. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_message_spec.py +0 -0
  114. {async_kernel-0.19.2 → async_kernel-0.20.0}/tests/test_typing.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@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
23
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
24
24
 
25
25
  - name: Type checking with basedpyright
26
26
  run: |
27
27
  uv sync
28
- uvx basedpyright@1.39.6
28
+ uvx basedpyright@1.39.8
29
29
 
30
30
  test:
31
31
  needs: type-checking
@@ -45,7 +45,7 @@ jobs:
45
45
  uses: actions/checkout@v6
46
46
 
47
47
  - name: Install uv
48
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
48
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
49
49
  with:
50
50
  version-file: "pyproject.toml"
51
51
  python-version: ${{ matrix.python-version }}
@@ -57,9 +57,26 @@ jobs:
57
57
  # Pyside does not have free-threaded binary
58
58
  run: uv sync --locked --dev --group gui
59
59
 
60
+ - name: CI debugging env windows
61
+ if: ${{runner.debug == '1' && startsWith(matrix.os, 'windows')}}
62
+ run: |
63
+ $Env:CI_DEBUGGING="1"
64
+
65
+ - name: CI debugging env non-windows
66
+ if: ${{runner.debug == '1' && !startsWith(matrix.os, 'windows')}}
67
+ run: |
68
+ export CI_DEBUGGING=1
69
+
70
+ - name: Run tests debug mode
71
+ if: ${{runner.debug == '1'}}
72
+ timeout-minutes: 3
73
+ run: |
74
+ uv run pytest -vvl --override-ini=log_cli_level=DEBUG --override-ini=log_cli=true
75
+
60
76
  - name: Run tests
61
- timeout-minutes: 5
62
- run: uv run pytest -vvl --maxfail=1 --reruns 1
77
+ if: ${{runner.debug != '1'}}
78
+ timeout-minutes: 3
79
+ run: uv run pytest -vvl
63
80
 
64
81
  coverage:
65
82
  needs: type-checking
@@ -70,7 +87,7 @@ jobs:
70
87
  fetch-depth: 0
71
88
 
72
89
  - name: Install uv
73
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
90
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
74
91
  with:
75
92
  version-file: "pyproject.toml"
76
93
  python-version: "3.13"
@@ -84,7 +101,7 @@ jobs:
84
101
 
85
102
  - name: Upload coverage reports to Codecov
86
103
  if: ${{ !cancelled() }}
87
- uses: codecov/codecov-action@v6
104
+ uses: codecov/codecov-action@v7
88
105
  with:
89
106
  token: ${{ secrets.CODECOV_TOKEN }}
90
107
  slug: fleming79/async-kernel
@@ -99,7 +116,7 @@ jobs:
99
116
  fetch-depth: 0
100
117
 
101
118
  - name: Install uv
102
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
119
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
103
120
  with:
104
121
  version-file: "pyproject.toml"
105
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@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
34
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.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@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
30
+ uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.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@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
34
+ - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.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.1
31
+ rev: 0.37.3
32
32
  hooks:
33
33
  - id: check-github-workflows
34
34
 
35
35
  - repo: https://github.com/rbubley/mirrors-prettier
36
- rev: v3.8.3
36
+ rev: v3.8.4
37
37
  hooks:
38
38
  - id: prettier
39
39
  types_or: [yaml, html, json]
@@ -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.11
62
+ rev: v0.15.18
63
63
  hooks:
64
64
  - id: ruff-check
65
65
  types_or: [python, jupyter]
@@ -68,7 +68,7 @@ repos:
68
68
  types_or: [python, jupyter]
69
69
 
70
70
  - repo: https://github.com/scientific-python/cookie
71
- rev: 2026.04.04
71
+ rev: 2026.06.18
72
72
  hooks:
73
73
  - id: sp-repo-review
74
74
  additional_dependencies: ["repo-review[cli]"]
@@ -35,7 +35,8 @@
35
35
  "--notebook-dir=docs/notebooks",
36
36
  "--no-browser",
37
37
  "--IdentityProvider.token=''",
38
- "--port=9991"
38
+ "--port=9991",
39
+ "--KernelManager.transport_encryption=auto"
39
40
  ],
40
41
  "cwd": "${workspaceFolder}",
41
42
  "justMyCode": false,
@@ -5,6 +5,56 @@ 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.20.0] - 2026-06-22
9
+
10
+ ### <!-- 0 --> 🏗️ Breaking changes
11
+
12
+ - Interface startup refactoring [#491](https://github.com/fleming79/async-kernel/pull/491)
13
+
14
+ - Add IPApp [#480](https://github.com/fleming79/async-kernel/pull/480)
15
+
16
+ ### <!-- 1 --> 🚀 Features
17
+
18
+ - Startup and test client tweaks [#488](https://github.com/fleming79/async-kernel/pull/488)
19
+
20
+ - Caller id refactoring [#489](https://github.com/fleming79/async-kernel/pull/489)
21
+
22
+ - ZMQInterface simplification [#484](https://github.com/fleming79/async-kernel/pull/484)
23
+
24
+ - Support zmq curve encryption [#482](https://github.com/fleming79/async-kernel/pull/482)
25
+
26
+ ### <!-- 6 --> 🌀 Miscellaneous
27
+
28
+ - Use cancellation for interface shutdown. [#501](https://github.com/fleming79/async-kernel/pull/501)
29
+
30
+ - Use a Countdown event in _start_hb_iopub_shell_control_threads. [#500](https://github.com/fleming79/async-kernel/pull/500)
31
+
32
+ - Maintenance - update uv.lock and pre-commit [#499](https://github.com/fleming79/async-kernel/pull/499)
33
+
34
+ - Restore settings to start_kernel_callable_interface [#498](https://github.com/fleming79/async-kernel/pull/498)
35
+
36
+ - Change metadata 'supported_encryption' to a list and test against latest jupyter_client. [#497](https://github.com/fleming79/async-kernel/pull/497)
37
+
38
+ - BaseInterface and Caller shutdown tweaks [#496](https://github.com/fleming79/async-kernel/pull/496)
39
+
40
+ - Update uv.lock and pre-commit. [#495](https://github.com/fleming79/async-kernel/pull/495)
41
+
42
+ - Refactor SingleAsyncQueue and remove public 'queue' attribute [#494](https://github.com/fleming79/async-kernel/pull/494)
43
+
44
+ - Maintenance [#493](https://github.com/fleming79/async-kernel/pull/493)
45
+
46
+ - Revise kernel interrupts. [#490](https://github.com/fleming79/async-kernel/pull/490)
47
+
48
+ - Reliability fixes [#487](https://github.com/fleming79/async-kernel/pull/487)
49
+
50
+ - Add a stopping Pending on the Interface and add debugger shutdown. [#486](https://github.com/fleming79/async-kernel/pull/486)
51
+
52
+ - Maintenance [#485](https://github.com/fleming79/async-kernel/pull/485)
53
+
54
+ - Bump the actions group with 2 updates [#483](https://github.com/fleming79/async-kernel/pull/483)
55
+
56
+ - Maintenance [#481](https://github.com/fleming79/async-kernel/pull/481)
57
+
8
58
  ## [0.19.2] - 2026-06-04
9
59
 
10
60
  ### <!-- 0 --> 🏗️ Breaking changes
@@ -13,6 +63,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
63
 
14
64
  ### <!-- 6 --> 🌀 Miscellaneous
15
65
 
66
+ - Prepare for release v0.19.2 [#479](https://github.com/fleming79/async-kernel/pull/479)
67
+
16
68
  - Run do_execute in tasks [#478](https://github.com/fleming79/async-kernel/pull/478)
17
69
 
18
70
  ## [0.19.1] - 2026-05-27
@@ -1335,6 +1387,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1335
1387
 
1336
1388
  - Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
1337
1389
 
1390
+ [0.20.0]: https://github.com/fleming79/async-kernel/compare/v0.19.2..v0.20.0
1338
1391
  [0.19.2]: https://github.com/fleming79/async-kernel/compare/v0.19.1..v0.19.2
1339
1392
  [0.19.1]: https://github.com/fleming79/async-kernel/compare/v0.19.0..v0.19.1
1340
1393
  [0.19.0]: https://github.com/fleming79/async-kernel/compare/v0.18.3..v0.19.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async-kernel
3
- Version: 0.19.2
3
+ Version: 0.20.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
@@ -33,11 +33,11 @@ Classifier: Programming Language :: Python :: 3.15
33
33
  Classifier: Programming Language :: Python :: Free Threading :: 1 - Unstable
34
34
  Classifier: Typing :: Typed
35
35
  Requires-Python: >=3.11
36
- Requires-Dist: aiologic>=0.16.0
36
+ Requires-Dist: aiologic>=0.17.0
37
37
  Requires-Dist: anyio>=4.12
38
38
  Requires-Dist: comm>=0.2
39
39
  Requires-Dist: ipython>=9.0
40
- Requires-Dist: jupyter-client>=8.8; sys_platform != 'emscripten'
40
+ Requires-Dist: jupyter-client>=8.9.1; sys_platform != 'emscripten'
41
41
  Requires-Dist: jupyter-core>=5.9.1
42
42
  Requires-Dist: matplotlib-inline>0.1
43
43
  Requires-Dist: orjson>=3.10.16; sys_platform == 'emscripten'
@@ -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.19.2'
22
- __version_tuple__ = version_tuple = (0, 19, 2)
21
+ __version__ = version = '0.20.0'
22
+ __version_tuple__ = version_tuple = (0, 20, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -2,3 +2,4 @@
2
2
  :::async_kernel.interface.base
3
3
  :::async_kernel.interface.callable
4
4
  :::async_kernel.interface.zmq
5
+ :::async_kernel.interface.ip_app
@@ -38,7 +38,7 @@ requires-python = ">=3.11"
38
38
  dependencies = [
39
39
  "anyio>=4.12",
40
40
  "typing_extensions>=4.14",
41
- "aiologic>=0.16.0",
41
+ "aiologic>=0.17.0",
42
42
  "orjson>=3.10.16; sys_platform == 'emscripten'",
43
43
  "comm>=0.2",
44
44
  "ipython>=9.0",
@@ -48,8 +48,8 @@ dependencies = [
48
48
  "jupyter-core>=5.9.1",
49
49
  "sniffio>=1.3.0; sys_platform != 'emscripten'",
50
50
  "outcome; sys_platform != 'emscripten'",
51
- "pyzmq>=27.0; sys_platform != 'emscripten'", # pyzmq sockets (and threading) are not supported on pyodide (emscripten), `CallableInterface` is used for kernel messaging.
52
- "jupyter_client>=8.8; sys_platform != 'emscripten'",
51
+ "pyzmq>=27.0; sys_platform != 'emscripten'", # pyzmq sockets (and threading) are not supported on pyodide (emscripten), `CallableInterface` is used for kernel messaging.
52
+ "jupyter_client>=8.9.1; sys_platform != 'emscripten'",
53
53
  ]
54
54
 
55
55
  [project.urls]
@@ -76,24 +76,23 @@ docs = [
76
76
  "jupyterlab",
77
77
  "ipywidgets",
78
78
  "pandas",
79
- "ruff",
80
- "matplotlib"
79
+ "ruff>=0.15.17",
81
80
  ]
82
81
  test = [
83
82
  "pytest-cov>=7.0.0",
84
- "pytest-timeout",
85
83
  "pytest-mock",
86
- "pytest-rerunfailures",
87
- "pytest>=8.4,<9",
84
+ "pytest>=9,<10",
88
85
  "trio>=0.31.0",
89
- ]
86
+ ]
90
87
  uvloop = ["uvloop; python_version <= '3.13' and sys_platform != 'win32' and implementation_name == 'cpython'",
91
88
  "winloop; sys_platform == 'win32' and implementation_name == 'cpython'"
92
89
  ]
93
90
  dev = [
91
+ "matplotlib>=3.10.9; python_version < '3.15'",
94
92
  "debugpy",
95
93
  "ipykernel; implementation_name == 'cpython'",
96
94
  "pip; implementation_name == 'cpython'",
95
+ "jupyter-client",
97
96
  {include-group = "uvloop"},
98
97
  {include-group = "test"},
99
98
  ]
@@ -117,15 +116,17 @@ version-file = "_version.py"
117
116
  [tool.hatch.version.raw-options]
118
117
  local_scheme = "no-local-version"
119
118
 
120
- [tool.pytest.ini_options]
121
- minversion = "6.0"
119
+ [tool.pytest]
120
+ minversion = "9.0"
122
121
  xfail_strict = true
123
122
  log_cli_level = "INFO"
124
123
  log_level = "INFO"
125
124
  addopts = [
126
- "-raXs", "--durations=10", "--color=yes", "--doctest-modules",
127
- "--showlocals", "--strict-markers", "--strict-config",
128
- "-s",
125
+ "-raX",
126
+ "--color=yes",
127
+ "--strict-markers",
128
+ "--strict-config",
129
+ "--capture=no",
129
130
  ]
130
131
  testpaths = ["tests"]
131
132
 
@@ -150,7 +151,7 @@ source = ["async_kernel"]
150
151
  omit = ["tests/*", "hatch_build.py", '_version.py', "tk_host.py", "qt_host.py", "asyncio_guest.py", "attr_docs.py"]
151
152
 
152
153
  [tool.ruff]
153
- required-version = ">=0.12.8"
154
+ required-version = ">=0.15.16"
154
155
  line-length = 120
155
156
  extend-exclude = [
156
157
  "_version.py", "**/asyncio_guest.py"
@@ -248,3 +249,6 @@ reportUnknownLambdaType = false
248
249
 
249
250
  [tool.uv]
250
251
  required-version = "==0.11.16"
252
+
253
+ [tool.uv.sources]
254
+ jupyter-client = { git = "https://github.com/jupyter/jupyter_client.git" }
@@ -19,14 +19,13 @@ from typing import TYPE_CHECKING, Any, ClassVar, Literal, Self, Unpack, final
19
19
 
20
20
  import anyio
21
21
  from aiologic import BinarySemaphore, Event
22
- from aiologic.lowlevel import create_async_event, create_async_waiter, current_async_library
23
- from aiologic.meta import await_for
22
+ from aiologic.lowlevel import create_async_event, current_async_library
23
+ from aiologic.meta import await_for, iscoroutinelike
24
24
  from typing_extensions import override
25
25
  from wrapt import lazy_import
26
26
 
27
- import async_kernel.event_loop
28
27
  from async_kernel import utils
29
- from async_kernel.common import Fixed, KernelInterrupt, SingleAsyncQueue, noop
28
+ from async_kernel.common import Fixed, KernelInterrupt, SingleAsyncQueue
30
29
  from async_kernel.event_loop.run import Host, get_start_guest_run
31
30
  from async_kernel.pending import Pending, PendingGroup, PendingManager, PendingTracker
32
31
  from async_kernel.typing import Backend, CallerCreateOptions, CallerState, Hosts, NoValue, RunSettings, T
@@ -75,7 +74,7 @@ async def task_factory() -> AsyncGenerator[Callable[[contextvars.Context | None,
75
74
  coro = asyncio.sleep(0)
76
75
  tasks: set[asyncio.Task] = set()
77
76
  eager = False
78
- all_done = create_async_waiter(shield=True)
77
+ all_done = create_async_event(shield=True)
79
78
  active = True
80
79
  try:
81
80
  await loop.create_task(coro, eager_start=True) # pyright: ignore[reportCallIssue]
@@ -86,7 +85,7 @@ async def task_factory() -> AsyncGenerator[Callable[[contextvars.Context | None,
86
85
  def done_callback(task: asyncio.Task) -> None:
87
86
  tasks.discard(task)
88
87
  if not active and not tasks:
89
- all_done.wake()
88
+ all_done.set()
90
89
 
91
90
  def create_task(context: contextvars.Context | None, func: Callable, *args) -> None:
92
91
  if eager:
@@ -172,7 +171,7 @@ class Caller(anyio.AsyncContextManagerMixin):
172
171
  Set to 0 to disable (default when running tests).
173
172
  """
174
173
 
175
- CALLER_MAIN_THREAD_ID: int = id(threading.main_thread())
174
+ CALLER_MAIN_THREAD_ID: int = int(threading.main_thread().ident) # pyright: ignore[reportArgumentType]
176
175
 
177
176
  _caller_token = contextvars.ContextVar("caller_tokens", default=CALLER_MAIN_THREAD_ID)
178
177
  _instances: ClassVar[dict[int, Self]] = {}
@@ -416,8 +415,8 @@ class Caller(anyio.AsyncContextManagerMixin):
416
415
  except Exception as e:
417
416
  self.log.exception("Caller did not exit context nicely!", exc_info=e)
418
417
 
419
- if getattr(self, "_caller_id", None) is not None:
420
- # An event loop for the current thread.
418
+ if hasattr(self, "_thread"):
419
+ assert self._thread is threading.current_thread()
421
420
 
422
421
  if self.backend == Backend.asyncio:
423
422
  self._tasks.add(asyncio.create_task(run_caller_in_context()))
@@ -435,24 +434,32 @@ class Caller(anyio.AsyncContextManagerMixin):
435
434
 
436
435
  threading.Thread(target=to_thread, daemon=False).start()
437
436
  else:
438
- settings = RunSettings(
439
- backend=self.backend,
440
- host=self.host,
441
- backend_options=self.backend_options,
442
- host_options=self.host_options,
443
- )
444
-
445
- def run() -> None:
437
+
438
+ def async_kernel_caller() -> None:
439
+ self._thread, self._caller_id = threading.current_thread(), threading.get_ident()
446
440
  try:
447
- async_kernel.event_loop.run(run_caller_in_context, (), settings)
441
+ if not self.host:
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)
448
453
  except Exception as e:
449
- self.log.exception("A start_sync exception occurred.", exc_info=e)
450
- if not self.stopped:
454
+ if not self._stopping.done():
455
+ self.stop()
456
+ self.log.exception("%s exited early", self, exc_info=e)
451
457
  raise
452
458
 
453
- self._thread = threading.Thread(target=run, name=self.name or "async_kernel_caller", daemon=False)
454
- self._caller_id = id(self._thread)
455
- self._thread.start()
459
+ thread = threading.Thread(target=async_kernel_caller, name=self.name or None)
460
+ thread.start()
461
+ assert thread.ident
462
+ self._thread, self._caller_id = thread, thread.ident
456
463
 
457
464
  def stop(self, *, force: bool = False) -> CallerState:
458
465
  """
@@ -472,7 +479,7 @@ class Caller(anyio.AsyncContextManagerMixin):
472
479
  parent._worker_pool.remove(self)
473
480
  except ValueError:
474
481
  pass
475
- for func in tuple(self._queue_map):
482
+ for func in self._queue_map.copy():
476
483
  self.queue_close(func)
477
484
  if state is CallerState.initial and not self._children:
478
485
  self._stop_finalize()
@@ -500,8 +507,6 @@ class Caller(anyio.AsyncContextManagerMixin):
500
507
  raise RuntimeError(msg)
501
508
  socket = None
502
509
  try:
503
- if not self._name:
504
- self._name = threading.current_thread().name
505
510
  if self._zmq_context:
506
511
  socket = self._zmq_context.socket(1) # zmq.SocketType.PUB
507
512
  socket.connect(self.iopub_url)
@@ -554,7 +559,7 @@ class Caller(anyio.AsyncContextManagerMixin):
554
559
  e = None
555
560
  try:
556
561
  result = md["func"](*md["args"], **md["kwargs"])
557
- if inspect.iscoroutine(result):
562
+ if iscoroutinelike(result):
558
563
  if backend is Backend.asyncio:
559
564
  task = asyncio.current_task()
560
565
  assert task
@@ -597,7 +602,7 @@ class Caller(anyio.AsyncContextManagerMixin):
597
602
  else:
598
603
  try:
599
604
  result = item[0](*item[1], **item[2])
600
- if inspect.iscoroutine(result):
605
+ if iscoroutinelike(result):
601
606
  await result
602
607
  del result
603
608
  except Exception as e:
@@ -636,10 +641,9 @@ class Caller(anyio.AsyncContextManagerMixin):
636
641
 
637
642
  @classmethod
638
643
  def id_current(cls) -> int:
639
- "The id that is used for a caller for the current thread in CPython or context in Pyodide."
640
- if sys.platform == "emscripten":
641
- return cls._caller_token.get()
642
- return id(threading.current_thread())
644
+ "The immutable id of a caller for the current thread in CPython or context in Pyodide."
645
+
646
+ return cls._caller_token.get() if sys.platform == "emscripten" else threading.get_ident()
643
647
 
644
648
  @classmethod
645
649
  def get_existing(cls, caller_id: int | None = None, /) -> Self | None:
@@ -735,6 +739,9 @@ class Caller(anyio.AsyncContextManagerMixin):
735
739
  Pending: A pending that can be awaited to obtain the result of func.
736
740
  """
737
741
  pen = Pending(context, trackers, func=func, args=args, kwargs=kwargs, caller=self, **metadata)
742
+ if self._state in [CallerState.stopping, CallerState.stopped]:
743
+ pen.cancel(f"The caller has been stopped: {self}")
744
+ return pen
738
745
  if backend is NoValue or (backend := Backend(backend)) is self.backend:
739
746
  queue = self._queue
740
747
  elif not (queue := self._guest_queues.get(backend)):
@@ -787,9 +794,10 @@ class Caller(anyio.AsyncContextManagerMixin):
787
794
  """
788
795
 
789
796
  async def _call_later(*args: P.args, **kwargs: P.kwargs) -> T:
790
- if (delay_ := start_time - time.monotonic() + delay) > 0:
797
+ if (delay_ := start_time - time.monotonic() + delay) >= 0:
791
798
  await anyio.sleep(delay_)
792
- if inspect.iscoroutine(result := func(*args, **kwargs)):
799
+ result = func(*args, **kwargs)
800
+ if iscoroutinelike(result):
793
801
  result = await result
794
802
  return result # pyright: ignore[reportReturnType]
795
803
 
@@ -1003,6 +1011,10 @@ class Caller(anyio.AsyncContextManagerMixin):
1003
1011
  - Pass a container with all results when the limiter is not relevant.
1004
1012
  - `Caller.MAX_IDLE_POOL_INSTANCES`
1005
1013
  """
1014
+
1015
+ def noop() -> None:
1016
+ pass
1017
+
1006
1018
  resume = noop
1007
1019
  done: SingleAsyncQueue[Pending[T]] = SingleAsyncQueue()
1008
1020
  unfinished: set[Pending[T]] = set()
@@ -1034,7 +1046,7 @@ class Caller(anyio.AsyncContextManagerMixin):
1034
1046
  resume = noop
1035
1047
  else:
1036
1048
  done.append(pen)
1037
- if not done.queue and not unfinished:
1049
+ if len(done) == 0 and not unfinished:
1038
1050
  done.stop()
1039
1051
 
1040
1052
  pen_ = self.call_soon(scheduler)
@@ -1042,7 +1054,7 @@ class Caller(anyio.AsyncContextManagerMixin):
1042
1054
  async for pen in done:
1043
1055
  unfinished.discard(pen)
1044
1056
  yield pen
1045
- if pen_.done() and not unfinished and not done.queue:
1057
+ if pen_.done() and not unfinished and len(done) == 0:
1046
1058
  break
1047
1059
  elif max_concurrent_ and len(unfinished) < max_concurrent_:
1048
1060
  resume()
@@ -222,7 +222,7 @@ Tips:
222
222
  print("async-kernel", async_kernel.__version__)
223
223
  case Mode.start | Mode.help_all | Mode.show_config | Mode.show_config_json:
224
224
  launcher: InterfaceStartType = import_launcher(settings.get("launcher", ""))
225
- settings["--flags"] = flags
225
+ settings["flags"] = flags
226
226
  try:
227
227
  launcher(settings)
228
228
  except KeyboardInterrupt: