async-kernel 0.21.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.
Files changed (136) hide show
  1. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/ci.yml +4 -4
  2. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/new_release.yml +1 -1
  3. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/pre-commit.yml +1 -1
  4. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/publish-docs.yml +1 -1
  5. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/publish-to-pypi.yml +1 -1
  6. {async_kernel-0.21.0 → async_kernel-0.22.1}/.pre-commit-config.yaml +5 -5
  7. {async_kernel-0.21.0 → async_kernel-0.22.1}/.vscode/settings.json +4 -1
  8. {async_kernel-0.21.0 → async_kernel-0.22.1}/CHANGELOG.md +95 -1
  9. {async_kernel-0.21.0 → async_kernel-0.22.1}/PKG-INFO +15 -60
  10. {async_kernel-0.21.0 → async_kernel-0.22.1}/README.md +12 -57
  11. {async_kernel-0.21.0 → async_kernel-0.22.1}/_version.py +2 -2
  12. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/notebooks/caller.ipynb +18 -18
  13. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/notebooks/concurrency.ipynb +26 -17
  14. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/notebooks/custom_kernel.ipynb +4 -4
  15. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/interface.md +0 -2
  16. async_kernel-0.22.1/docs/reference/messaging.md +3 -0
  17. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/usage/commands.md +2 -2
  18. {async_kernel-0.21.0 → async_kernel-0.22.1}/mkdocs.yml +2 -0
  19. async_kernel-0.22.1/pyproject.toml +273 -0
  20. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/caller.py +523 -276
  21. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/comm.py +13 -15
  22. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/command.py +2 -4
  23. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/common.py +11 -24
  24. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/compat/attr_docs.py +0 -1
  25. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/compat/json.py +5 -7
  26. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/compiler.py +2 -4
  27. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/debugger.py +9 -11
  28. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/run.py +7 -14
  29. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/tk_host.py +3 -7
  30. async_kernel-0.22.1/src/async_kernel/event_loop/zmq_poll.py +442 -0
  31. async_kernel-0.22.1/src/async_kernel/interface/__init__.py +25 -0
  32. async_kernel-0.22.1/src/async_kernel/interface/base.py +549 -0
  33. async_kernel-0.22.1/src/async_kernel/interface/callable.py +117 -0
  34. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/interface/ip_app.py +14 -28
  35. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/kernel.py +111 -101
  36. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/kernelspec.py +9 -13
  37. async_kernel-0.22.1/src/async_kernel/messaging/__init__.py +3 -0
  38. async_kernel-0.22.1/src/async_kernel/messaging/base.py +462 -0
  39. async_kernel-0.22.1/src/async_kernel/messaging/zmq.py +423 -0
  40. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/outstream.py +4 -11
  41. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/pending.py +56 -82
  42. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/shell/base.py +10 -19
  43. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/shell/ipshell.py +66 -88
  44. async_kernel-0.22.1/src/async_kernel/typing.py +615 -0
  45. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/utils.py +30 -39
  46. async_kernel-0.22.1/tests/conftest.py +101 -0
  47. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/references.py +26 -26
  48. async_kernel-0.22.1/tests/test_callable_interface.py +75 -0
  49. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_caller.py +322 -121
  50. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_comm.py +2 -1
  51. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_command.py +23 -71
  52. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_common.py +2 -1
  53. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_compat.py +4 -8
  54. async_kernel-0.22.1/tests/test_connection.py +69 -0
  55. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_debugger.py +15 -22
  56. async_kernel-0.22.1/tests/test_debugger_static.py +59 -0
  57. async_kernel-0.22.1/tests/test_enter_kernel.py +39 -0
  58. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_event_loop.py +3 -3
  59. async_kernel-0.22.1/tests/test_eventloop_zmq_poll.py +250 -0
  60. async_kernel-0.21.0/tests/test_base_interface.py → async_kernel-0.22.1/tests/test_interface.py +64 -21
  61. async_kernel-0.22.1/tests/test_ipapp.py +41 -0
  62. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_kernel_ipshell.py +127 -148
  63. async_kernel-0.22.1/tests/test_message_spec.py +283 -0
  64. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_outstream.py +14 -8
  65. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_pending.py +45 -3
  66. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_subclass.py +3 -3
  67. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_typing.py +22 -0
  68. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_utils.py +2 -5
  69. async_kernel-0.22.1/tests/test_zmq_connection.py +179 -0
  70. async_kernel-0.22.1/tests/test_zmq_subprocess.py +127 -0
  71. async_kernel-0.22.1/tests/utils.py +80 -0
  72. {async_kernel-0.21.0 → async_kernel-0.22.1}/uv.lock +724 -586
  73. async_kernel-0.21.0/pyproject.toml +0 -254
  74. async_kernel-0.21.0/src/async_kernel/event_loop/zmq_poll.py +0 -228
  75. async_kernel-0.21.0/src/async_kernel/interface/__init__.py +0 -64
  76. async_kernel-0.21.0/src/async_kernel/interface/base.py +0 -543
  77. async_kernel-0.21.0/src/async_kernel/interface/callable.py +0 -151
  78. async_kernel-0.21.0/src/async_kernel/interface/zmq.py +0 -282
  79. async_kernel-0.21.0/src/async_kernel/typing.py +0 -474
  80. async_kernel-0.21.0/tests/conftest.py +0 -194
  81. async_kernel-0.21.0/tests/test_callable_interface.py +0 -97
  82. async_kernel-0.21.0/tests/test_debugger_static.py +0 -67
  83. async_kernel-0.21.0/tests/test_encryption.py +0 -29
  84. async_kernel-0.21.0/tests/test_enter_kernel.py +0 -24
  85. async_kernel-0.21.0/tests/test_eventloop_zmq_poll.py +0 -137
  86. async_kernel-0.21.0/tests/test_message_spec.py +0 -338
  87. async_kernel-0.21.0/tests/test_zmq_interface.py +0 -211
  88. async_kernel-0.21.0/tests/test_zmq_interface_ip.py +0 -61
  89. async_kernel-0.21.0/tests/utils.py +0 -209
  90. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/dependabot.yaml +0 -0
  91. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/release.yml +0 -0
  92. {async_kernel-0.21.0 → async_kernel-0.22.1}/.github/workflows/enforce-label.yml +0 -0
  93. {async_kernel-0.21.0 → async_kernel-0.22.1}/.gitignore +0 -0
  94. {async_kernel-0.21.0 → async_kernel-0.22.1}/.vscode/launch.json +0 -0
  95. {async_kernel-0.21.0 → async_kernel-0.22.1}/.vscode/spellright.dict +0 -0
  96. {async_kernel-0.21.0 → async_kernel-0.22.1}/CONTRIBUTING.md +0 -0
  97. {async_kernel-0.21.0 → async_kernel-0.22.1}/IPYTHON_LICENSE +0 -0
  98. {async_kernel-0.21.0 → async_kernel-0.22.1}/LICENSE +0 -0
  99. {async_kernel-0.21.0 → async_kernel-0.22.1}/cliff.toml +0 -0
  100. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/about/changelog.md +0 -0
  101. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/about/contributing.md +0 -0
  102. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/about/index.md +0 -0
  103. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/about/license.md +0 -0
  104. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/index.md +0 -0
  105. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/javascripts/extra.js +0 -0
  106. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/overrides/main.html +0 -0
  107. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/caller.md +0 -0
  108. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/comm.md +0 -0
  109. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/command.md +0 -0
  110. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/common.md +0 -0
  111. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/debugger.md +0 -0
  112. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/event_loop.md +0 -0
  113. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/index.md +0 -0
  114. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/ipshell.md +0 -0
  115. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/kernel.md +0 -0
  116. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/kernelspec.md +0 -0
  117. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/pending.md +0 -0
  118. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/shell.md +0 -0
  119. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/typing.md +0 -0
  120. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/reference/utils.md +0 -0
  121. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/stylesheets/extra.css +0 -0
  122. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/thread_safety.md +0 -0
  123. {async_kernel-0.21.0 → async_kernel-0.22.1}/docs/usage/index.md +0 -0
  124. {async_kernel-0.21.0 → async_kernel-0.22.1}/hatch_build.py +0 -0
  125. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/__init__.py +0 -0
  126. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/__main__.py +0 -0
  127. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/__init__.py +0 -0
  128. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
  129. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/event_loop/qt_host.py +0 -0
  130. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/py.typed +0 -0
  131. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-32x32.png +0 -0
  132. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-64x64.png +0 -0
  133. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/resources/logo-svg.svg +0 -0
  134. {async_kernel-0.21.0 → async_kernel-0.22.1}/src/async_kernel/shell/__init__.py +0 -0
  135. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/__init__.py +0 -0
  136. {async_kernel-0.21.0 → async_kernel-0.22.1}/tests/test_kernelspec.py +0 -0
@@ -20,7 +20,7 @@ jobs:
20
20
  fetch-depth: 0
21
21
 
22
22
  - name: Install uv
23
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
23
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
24
24
 
25
25
  - name: Type checking with basedpyright
26
26
  run: |
@@ -45,7 +45,7 @@ jobs:
45
45
  uses: actions/checkout@v7
46
46
 
47
47
  - name: Install uv
48
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
48
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
49
49
  with:
50
50
  version-file: "pyproject.toml"
51
51
  python-version: ${{ matrix.python-version }}
@@ -87,7 +87,7 @@ jobs:
87
87
  fetch-depth: 0
88
88
 
89
89
  - name: Install uv
90
- uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
90
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
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@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
119
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
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@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
34
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
35
35
  with:
36
36
  version-file: "pyproject.toml"
37
37
 
@@ -8,7 +8,7 @@ jobs:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
10
  - uses: actions/checkout@v7
11
- - uses: actions/setup-python@v6
11
+ - uses: actions/setup-python@v7
12
12
  with:
13
13
  python-version: 3.x
14
14
  - uses: pre-commit/action@v3.0.1
@@ -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@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
30
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
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@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0
34
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
35
35
  with:
36
36
  version-file: "pyproject.toml"
37
37
 
@@ -28,15 +28,15 @@ repos:
28
28
  - id: check-json5
29
29
 
30
30
  - repo: https://github.com/python-jsonschema/check-jsonschema
31
- rev: 0.37.4
31
+ rev: 0.38.0
32
32
  hooks:
33
33
  - id: check-github-workflows
34
34
 
35
35
  - repo: https://github.com/rbubley/mirrors-prettier
36
- rev: v3.9.5
36
+ rev: v3.9.6
37
37
  hooks:
38
38
  - id: prettier
39
- types_or: [yaml, html, json]
39
+ types_or: [yaml, html, json, toml]
40
40
  - id: prettier
41
41
  types_or: [markdown]
42
42
  args: ["--tab-width", "4"]
@@ -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.22
62
+ rev: v0.16.3
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.06.18
71
+ rev: 2026.08.14
72
72
  hooks:
73
73
  - id: sp-repo-review
74
74
  additional_dependencies: ["repo-review[cli]"]
@@ -18,5 +18,8 @@
18
18
  "tag:yaml.org,2002:python/object/apply:pymdownx.slugs.slugify mapping"
19
19
  ],
20
20
  "spellright.language": ["en"],
21
- "spellright.documentTypes": ["markdown", "plaintext"]
21
+ "spellright.documentTypes": ["markdown", "plaintext"],
22
+ "[toml]": {
23
+ "editor.defaultFormatter": "tamasfe.even-better-toml"
24
+ }
22
25
  }
@@ -5,7 +5,97 @@ 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.21.0] - 2026-07-18
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
+
22
+ ## [0.22.0] - 2026-08-17
23
+
24
+ ### <!-- 0 --> 🏗️ Breaking changes
25
+
26
+ - Major interface refactoring, new features and capabilities [#538](https://github.com/fleming79/async-kernel/pull/538)
27
+
28
+ ### <!-- 1 --> 🚀 Features
29
+
30
+ - Add ShieldedTask. [#553](https://github.com/fleming79/async-kernel/pull/553)
31
+
32
+ - Change caller async context manager to be first in last out, intermediate contexts are not managed. [#549](https://github.com/fleming79/async-kernel/pull/549)
33
+
34
+ - Add ProtectedPending to prevent accidental propagation of cancellation of a cancelled waiter. [#546](https://github.com/fleming79/async-kernel/pull/546)
35
+
36
+ - Add cancellation support in ZMQPoll. [#541](https://github.com/fleming79/async-kernel/pull/541)
37
+
38
+ - Rename Poll to ZMQPoll and add ZMQPollSocket [#539](https://github.com/fleming79/async-kernel/pull/539)
39
+
40
+ - Caller async context management changes [#537](https://github.com/fleming79/async-kernel/pull/537)
41
+
42
+ - Split BaseInterface into BaseMessageApplication [#536](https://github.com/fleming79/async-kernel/pull/536)
43
+
44
+ ### <!-- 2 --> 🐛 Fixes
45
+
46
+ - Fix heartbeat [#532](https://github.com/fleming79/async-kernel/pull/532)
47
+
48
+ ### <!-- 5 --> 📝 Documentation
49
+
50
+ - Use Ruff lint pydocstyle [#531](https://github.com/fleming79/async-kernel/pull/531)
51
+
52
+ ### <!-- 6 --> 🌀 Miscellaneous
53
+
54
+ - Prepare for release v0.22.0 [#566](https://github.com/fleming79/async-kernel/pull/566)
55
+
56
+ - Bump uv from 0.11.32 to 0.12.4 [#562](https://github.com/fleming79/async-kernel/pull/562)
57
+
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)
59
+
60
+ - Pre-commit autoupdate and update uv.lock. [#565](https://github.com/fleming79/async-kernel/pull/565)
61
+
62
+ - Fix pyright type hinting of NoValue. [#564](https://github.com/fleming79/async-kernel/pull/564)
63
+
64
+ - Rename ShieldedTask to StartStopTask. [#557](https://github.com/fleming79/async-kernel/pull/557)
65
+
66
+ - More features fro ShieldedTask. [#556](https://github.com/fleming79/async-kernel/pull/556)
67
+
68
+ - Compatibility fix with ipython. [#555](https://github.com/fleming79/async-kernel/pull/555)
69
+
70
+ - Change ShieldedTask to use set_task_function to set the function and caller to avoid clashing when used as a mixin. [#554](https://github.com/fleming79/async-kernel/pull/554)
71
+
72
+ - Refactor Pending._set_done. [#552](https://github.com/fleming79/async-kernel/pull/552)
73
+
74
+ - Refactor Pending._set_done. [#551](https://github.com/fleming79/async-kernel/pull/551)
75
+
76
+ - Improve Caller auto shutdown support [#550](https://github.com/fleming79/async-kernel/pull/550)
77
+
78
+ - Make Caller a bit safer for shutdown where it's context is held inside a pending task. [#548](https://github.com/fleming79/async-kernel/pull/548)
79
+
80
+ - Improve caller shutdown management and tests [#547](https://github.com/fleming79/async-kernel/pull/547)
81
+
82
+ - Bump astral-sh/setup-uv from 8.3.2 to 9.0.0 in the actions group [#544](https://github.com/fleming79/async-kernel/pull/544)
83
+
84
+ - ZMQPoll support multiple context entry and re-entry. [#545](https://github.com/fleming79/async-kernel/pull/545)
85
+
86
+ - Improve kernel shutdown. [#543](https://github.com/fleming79/async-kernel/pull/543)
87
+
88
+ - More Messge types and general fixes. [#542](https://github.com/fleming79/async-kernel/pull/542)
89
+
90
+ - Improve ZMQPollSocket autoclose. [#540](https://github.com/fleming79/async-kernel/pull/540)
91
+
92
+ - Fix the ident of input request messages. [#535](https://github.com/fleming79/async-kernel/pull/535)
93
+
94
+ - Bump the actions group with 2 updates [#533](https://github.com/fleming79/async-kernel/pull/533)
95
+
96
+ - Improve uv magic. [#534](https://github.com/fleming79/async-kernel/pull/534)
97
+
98
+ ## [0.21.0] - 2026-07-19
9
99
 
10
100
  ### <!-- 1 --> 🚀 Features
11
101
 
@@ -17,6 +107,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
107
 
18
108
  ### <!-- 6 --> 🌀 Miscellaneous
19
109
 
110
+ - Prepare for release v0.21.0 [#530](https://github.com/fleming79/async-kernel/pull/530)
111
+
20
112
  - Poll maintenance [#529](https://github.com/fleming79/async-kernel/pull/529)
21
113
 
22
114
  - Add threadsafe option to Poll.socket [#528](https://github.com/fleming79/async-kernel/pull/528)
@@ -1453,6 +1545,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1453
1545
 
1454
1546
  - Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
1455
1547
 
1548
+ [0.22.1]: https://github.com/fleming79/async-kernel/compare/v0.22.0..v0.22.1
1549
+ [0.22.0]: https://github.com/fleming79/async-kernel/compare/v0.21.0..v0.22.0
1456
1550
  [0.21.0]: https://github.com/fleming79/async-kernel/compare/v0.20.2..v0.21.0
1457
1551
  [0.20.2]: https://github.com/fleming79/async-kernel/compare/v0.20.1..v0.20.2
1458
1552
  [0.20.1]: https://github.com/fleming79/async-kernel/compare/v0.20.0..v0.20.1
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: async-kernel
3
- Version: 0.21.0
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
@@ -36,7 +36,7 @@ Requires-Python: >=3.11
36
36
  Requires-Dist: aiologic>=0.17.0
37
37
  Requires-Dist: anyio>=4.12
38
38
  Requires-Dist: comm>=0.2
39
- Requires-Dist: ipython>=9.0
39
+ Requires-Dist: ipython>=9.16.1
40
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
@@ -62,22 +62,24 @@ Description-Content-Type: text/markdown
62
62
 
63
63
  ![logo-svg](https://github.com/user-attachments/assets/6781ec08-94e9-4640-b8f9-bb07a08e9587)
64
64
 
65
- async-kernel is a Python kernel for [Jupyter](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages)
66
- that provides concurrent message handling via an asynchronous backend (asyncio or trio).
65
+ async-kernel provides Python [Jupyter](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages) kernels and clients
66
+ compatible with CPython (Jupyter & VS code) and Pyodide (Jupyterlite).
67
67
 
68
- The kernel provides two external interfaces:
68
+ The kernel interface supports multiple connections including:
69
69
 
70
- 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VS Code, etc).
71
- 2. An experimental callback style interface (Jupyterlite).
70
+ 1. Messaging via ZMQ sockets (Jupyter, VS Code, etc).
71
+ 2. Same-process local client enabling (Jupyterlite) and user access.
72
72
 
73
73
  ## Highlights
74
74
 
75
+ - Built using [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives.
76
+ - The [`Caller`](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
77
+ class provides a powerful but simple interface for cross-thread code execution in asyncio and trio
78
+ backends, with guest event loop support built in.
79
+ - A zmq poll event loop for thread-safe zmq sockets.
75
80
  - [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
76
81
  - top-level await ('asyncio' or 'trio' backend) in cells
77
82
  - async magic function support in cells
78
- - [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
79
- - [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
80
- - [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
81
83
  - Per-subshell user_ns
82
84
  - GUI event loops [^1]
83
85
  - [x] inline
@@ -88,6 +90,8 @@ The kernel provides two external interfaces:
88
90
  [Jupyterlite](https://github.com/jupyterlite/jupyterlite) (try it online [here](https://fleming79.github.io/echo-kernel/) 👈)
89
91
  - `%pip install` magic (using micropip)
90
92
  - [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
93
+ - Local client.
94
+ - ZMQ client.
91
95
 
92
96
  [^1]:
93
97
  A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
@@ -100,55 +104,6 @@ The kernel provides two external interfaces:
100
104
 
101
105
  [^3]: trio's [start_guest_run](https://trio.readthedocs.io/en/stable/reference-lowlevel.html#trio.lowlevel.start_guest_run).
102
106
 
103
- ### Avoid deadlocks
104
-
105
- The standard (synchronous) kernel implementation processes messages sequentially irrespective
106
- of the message type. The problem being that long running execute requests make the kernel non-responsive.
107
-
108
- Another problem exists when an asynchronous execute request awaits a result that is delivered
109
- via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
110
- the _blocking_ execute request[^5].
111
-
112
- async-kernel handles messages according to the channel and message type. So widget com message
113
- will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
114
-
115
- #### Example
116
-
117
- Make a blocking call in a Jupyter lab notebook or console.
118
-
119
- ```python
120
- # Make the shell's thread busy
121
- import time
122
-
123
- time.sleep(1e6)
124
- ```
125
-
126
- While the above is _blocking_ (the kernel is _busy_).
127
-
128
- ```python
129
- dir() # try code completion (tab) or view the docstring (shift tab)
130
- ```
131
-
132
- Interrupt the kernel.
133
-
134
- It also works for awaitables.
135
-
136
- ```python
137
- import ipywidgets as ipw
138
- from aiologic import Event
139
-
140
- b = ipw.Button(description="Click me")
141
- event = Event()
142
- b.on_click(lambda _: event.set())
143
- display(b)
144
- await event
145
- ```
146
-
147
- [^5]:
148
- IPyKernel _solves_ this issue specifically for widgets by using the concept of
149
- 'widget coms over subshells'. Widget messages arrive in a different thread which on
150
- occasion can cause unexpected behaviour, especially when using asynchronous libraries.
151
-
152
107
  ## Installation
153
108
 
154
109
  ```bash
@@ -11,22 +11,24 @@
11
11
 
12
12
  ![logo-svg](https://github.com/user-attachments/assets/6781ec08-94e9-4640-b8f9-bb07a08e9587)
13
13
 
14
- async-kernel is a Python kernel for [Jupyter](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages)
15
- that provides concurrent message handling via an asynchronous backend (asyncio or trio).
14
+ async-kernel provides Python [Jupyter](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages) kernels and clients
15
+ compatible with CPython (Jupyter & VS code) and Pyodide (Jupyterlite).
16
16
 
17
- The kernel provides two external interfaces:
17
+ The kernel interface supports multiple connections including:
18
18
 
19
- 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VS Code, etc).
20
- 2. An experimental callback style interface (Jupyterlite).
19
+ 1. Messaging via ZMQ sockets (Jupyter, VS Code, etc).
20
+ 2. Same-process local client enabling (Jupyterlite) and user access.
21
21
 
22
22
  ## Highlights
23
23
 
24
+ - Built using [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives.
25
+ - The [`Caller`](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
26
+ class provides a powerful but simple interface for cross-thread code execution in asyncio and trio
27
+ backends, with guest event loop support built in.
28
+ - A zmq poll event loop for thread-safe zmq sockets.
24
29
  - [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
25
30
  - top-level await ('asyncio' or 'trio' backend) in cells
26
31
  - async magic function support in cells
27
- - [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
28
- - [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
29
- - [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
30
32
  - Per-subshell user_ns
31
33
  - GUI event loops [^1]
32
34
  - [x] inline
@@ -37,6 +39,8 @@ The kernel provides two external interfaces:
37
39
  [Jupyterlite](https://github.com/jupyterlite/jupyterlite) (try it online [here](https://fleming79.github.io/echo-kernel/) 👈)
38
40
  - `%pip install` magic (using micropip)
39
41
  - [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
42
+ - Local client.
43
+ - ZMQ client.
40
44
 
41
45
  [^1]:
42
46
  A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
@@ -49,55 +53,6 @@ The kernel provides two external interfaces:
49
53
 
50
54
  [^3]: trio's [start_guest_run](https://trio.readthedocs.io/en/stable/reference-lowlevel.html#trio.lowlevel.start_guest_run).
51
55
 
52
- ### Avoid deadlocks
53
-
54
- The standard (synchronous) kernel implementation processes messages sequentially irrespective
55
- of the message type. The problem being that long running execute requests make the kernel non-responsive.
56
-
57
- Another problem exists when an asynchronous execute request awaits a result that is delivered
58
- via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
59
- the _blocking_ execute request[^5].
60
-
61
- async-kernel handles messages according to the channel and message type. So widget com message
62
- will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
63
-
64
- #### Example
65
-
66
- Make a blocking call in a Jupyter lab notebook or console.
67
-
68
- ```python
69
- # Make the shell's thread busy
70
- import time
71
-
72
- time.sleep(1e6)
73
- ```
74
-
75
- While the above is _blocking_ (the kernel is _busy_).
76
-
77
- ```python
78
- dir() # try code completion (tab) or view the docstring (shift tab)
79
- ```
80
-
81
- Interrupt the kernel.
82
-
83
- It also works for awaitables.
84
-
85
- ```python
86
- import ipywidgets as ipw
87
- from aiologic import Event
88
-
89
- b = ipw.Button(description="Click me")
90
- event = Event()
91
- b.on_click(lambda _: event.set())
92
- display(b)
93
- await event
94
- ```
95
-
96
- [^5]:
97
- IPyKernel _solves_ this issue specifically for widgets by using the concept of
98
- 'widget coms over subshells'. Widget messages arrive in a different thread which on
99
- occasion can cause unexpected behaviour, especially when using asynchronous libraries.
100
-
101
56
  ## Installation
102
57
 
103
58
  ```bash
@@ -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.21.0'
22
- __version_tuple__ = version_tuple = (0, 21, 0)
21
+ __version__ = version = '0.22.1'
22
+ __version_tuple__ = version_tuple = (0, 22, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -13,9 +13,9 @@
13
13
  "source": [
14
14
  "# Caller: A backend agnostic executor (notebook)\n",
15
15
  "\n",
16
- "_This notebook has the Python dependencies: `trio`, `matplotlib`_\n",
16
+ "_This notebook uses `trio` in some cells._\n",
17
17
  "\n",
18
- "`Caller` is a thread-local class that coordinates the execution of functions and coroutines in the event loop of a `asyncio` or `trio` backend."
18
+ "`Caller` is a thread-local object that coordinates the execution of functions and coroutines in the event loop of an `asyncio` or `trio` backend."
19
19
  ]
20
20
  },
21
21
  {
@@ -36,13 +36,13 @@
36
36
  "source": [
37
37
  "A `Caller` instance can be started with one gui as a host to one backend started as a guest. \n",
38
38
  "\n",
39
- "A second backend can also be started in the same thread with or without a host gui event loop.\n",
39
+ "A second backend can also be started in the same thread/caller with or without a host gui event loop.\n",
40
40
  "Potentially the same thread could have up to:\n",
41
41
  "\n",
42
42
  "- One host gui event loop (`tk`, `qt`, `...`)\n",
43
43
  "- Two asynchronous backends (`asyncio`, `trio`)\n",
44
44
  "\n",
45
- "It should be noted that gui's generally assume/require their mainloop runs in the main thread."
45
+ "It should be noted that gui's generally assume/require that their mainloop runs in the main thread."
46
46
  ]
47
47
  },
48
48
  {
@@ -74,7 +74,7 @@
74
74
  "id": "5",
75
75
  "metadata": {},
76
76
  "source": [
77
- "There are two caller instances associated with the Kernel:\n",
77
+ "There are two caller instances associated with the kernel interface:\n",
78
78
  "\n",
79
79
  "- Shell: The caller for the shell channel (normally the MainThread).\n",
80
80
  "- Control: The caller for the control channel (a child of the shell)."
@@ -109,12 +109,11 @@
109
109
  "source": [
110
110
  "## Modifiers\n",
111
111
  "\n",
112
- "There are four modifiers that determine which instance is returned when calling `Caller`.\n",
112
+ "There are three modifiers that can change the instance that is returned when calling `Caller`.\n",
113
113
  "\n",
114
114
  "- `CurrentThread` (default): Access the caller that belongs to the current thread (or context with Pyodide).\n",
115
115
  "- `MainThread`: Access main thread for the Caller.\n",
116
- "- `NewThread`: Create a new thread.\n",
117
- "- `manual`: Manually create a new instance for the current thread (must be started manually)."
116
+ "- `NewThread`: Create a new thread."
118
117
  ]
119
118
  },
120
119
  {
@@ -186,9 +185,11 @@
186
185
  "source": [
187
186
  "## Caller.get\n",
188
187
  "\n",
189
- "`Caller.get` is a method to get an instance of a `Caller` by name, passing the same name will return the same caller on subsequent calls.\n",
190
- "The same [options](#caller-creation-options) for creation of a new `Caller` can be specified. Calling `caller.get()` with no `name` will always return a\n",
191
- "new instance of a `Caller`."
188
+ "`Caller.get` is a method to obtain a _child_ instance of a `Caller` by name. \n",
189
+ "Passing the same name on the same caller will return the same child caller on subsequent calls.\n",
190
+ "The same [options](#caller-creation-options) for creation of a new `Caller` can be specified. \n",
191
+ "Calling `caller.get()` with no `name` will always return a new instance of a `Caller`. All children\n",
192
+ "of a caller will stop when the parent stops."
192
193
  ]
193
194
  },
194
195
  {
@@ -319,7 +320,7 @@
319
320
  "\n",
320
321
  "### Caller.queue_call\n",
321
322
  "\n",
322
- "This method is used extensively by the kernel for message handling. Each time the `queue_call` is called with the same function the function and call arguments are appended to a queue. On the first call a task is started in the backend to process the queue as items are added. The task remains alive until the function is deleted or the queue is stopped. Any exceptions that occur are logged."
323
+ "This method is used extensively by the kernel for message handling. Each time the `queue_call` is called with the same function, the function and call arguments are appended to a queue. On the first call a task is started in the backend to process the queue as items are added. The task remains alive until the function is deleted or the queue is stopped. Any exceptions that occur are logged."
323
324
  ]
324
325
  },
325
326
  {
@@ -338,7 +339,7 @@
338
339
  "def f(n):\n",
339
340
  " float(n)\n",
340
341
  " done.down()\n",
341
- " print(done)\n",
342
+ " print(done.value, \"Pending id:\", id(Caller.current_pending()))\n",
342
343
  "\n",
343
344
  "\n",
344
345
  "for i in range(N):\n",
@@ -413,13 +414,11 @@
413
414
  "import random\n",
414
415
  "import time\n",
415
416
  "\n",
416
- "import anyio\n",
417
417
  "import ipywidgets as ipw\n",
418
418
  "\n",
419
419
  "outputs = {}\n",
420
420
  "stop = ipw.RadioButtons(value=None, options=[\"Stop\"])\n",
421
421
  "box = ipw.VBox([stop])\n",
422
- "await anyio.sleep(0.2)\n",
423
422
  "display(box)\n",
424
423
  "\n",
425
424
  "\n",
@@ -476,13 +475,14 @@
476
475
  "source": [
477
476
  "## Caller.create_pending_group\n",
478
477
  "\n",
479
- "A `PendingGroup` is an asynchronous context manager for for `Pending`. All pending that 'opt-in' when created are added to the pending group (`Caller` methods that return `Pending` 'opt-in').\n",
478
+ "A `PendingGroup` is an asynchronous context manager for `Pending`. All pending that 'opt-in' when created are added to the pending group (`Caller` methods that return `Pending` 'opt-in').\n",
480
479
  "\n",
481
480
  "The `PendingGroup` has different modes:\n",
482
481
  "\n",
483
- "- 0: Ignore cancellation of pending.\n",
482
+ "- 0: Ignore cancellation of pending, if any pending is cancelled - exit quietly.\n",
484
483
  "- 1: Cancel if any pending is cancelled - raise PendingCancelled on exit.\n",
485
- "- 2: Cancel if any pending is cancelled - exit quietly."
484
+ "- 2: Cancel if any pending is cancelled - exit quietly.\n",
485
+ "- 3: Ignore cancellation of pending, if any pending is cancelled - raise PendingCancelled on exit."
486
486
  ]
487
487
  },
488
488
  {
@@ -13,32 +13,41 @@
13
13
  "source": [
14
14
  "# Concurrent message handling (notebook)\n",
15
15
  "\n",
16
- "async-kernel is capable of concurrent message handling. It provides a separate message handler for each channel and msg_type.\n",
17
- "\n",
18
- "As messages are received they are queued for execution by the message handler.\n",
16
+ "async-kernel provides concurrent message handling with a separate message handler queue for each channel and msg_type.\n",
17
+ "Execute requests also provide a separate queue per subshell.\n",
19
18
  "\n",
20
19
  "## Callers\n",
21
20
  "\n",
22
21
  "async-kernel provides two `Callers` for the _shell_ and _control_ channels. The shell's `Caller` is \n",
23
22
  "associated with the thread where the kernel is started, normally this is the _MainThread_. \n",
24
- "In CPython, the `Caller` for the _control_ channel is started as a child of the shell caller with \n",
25
- "it's own thread and asynchronous backend ('asyncio' or 'trio').\n",
26
23
  "\n",
27
- "## ZMQ message handling (CPython)\n",
24
+ "## Interface\n",
25
+ "\n",
26
+ "The interface is a singleton which provides configuration for all classes that subclass from `HasInterface`.\n",
27
+ "The interface coordinates the startup and shutdown of the kernel and connections to communicate with the\n",
28
+ "kernel.\n",
29
+ "\n",
30
+ "In CPython the interface can be started from the command line with a connection file. The interface\n",
31
+ "is configurable via settings defined in the kernel spec, or by using the traitlets style configuration\n",
32
+ "files. Multiple connections are allow on the connection. \n",
33
+ "\n",
34
+ "## Connections \n",
35
+ "Connections are provided to communicate with the kernel in the same thread. There is\n",
36
+ "no limit to the number of connections, however increasing the number of connections will slow\n",
37
+ "down the interface because all iopub messages are sent on every connection. Additional connection\n",
38
+ "subclasses can created by subclassing from Connection.\n",
39
+ "\n",
40
+ "\n",
41
+ "## ZMQ Socket Based connection\n",
28
42
  "\n",
29
- "The kernel interface also starts two additional threads dedicated to receive the messages on \n",
30
- "the _shell_ and _control_ channels, the specific function call is `session.recv(socket, mode=zmq.BLOCKY, copy=False)`. \n",
43
+ "Currently zmq sockets are the only _external_ connection provided. A ZMQConnection is automatically\n",
44
+ "started when the interface is started in CPython.\n",
31
45
  "\n",
32
- "Each thread blocks until a new message is received on the zmq socket. When a message is received, \n",
33
- "the kernel's `message_handler` is called with detail of the message (Job), the channel and a function\n",
34
- "that is to be used to send the response corresponding to the message. The `message_handler` obtains \n",
35
- "the dedicated handler according to the `message_type`, `run_mode`, `channel` and `subshell_id` and \n",
36
- "schedules execution of the dedicated message handler in `Caller's` thread. \n",
37
46
  "\n",
38
47
  "## Shell messaging\n",
39
48
  "\n",
40
- "Both `execute_request` and `com_msg` are always run in the shell's thread (normally the _MainThread_).\n",
41
- "All other messages on the shell channel are run in the control thread.\n",
49
+ "Both `execute_request` and `com_msg` are always handled in the shell's thread (normally the _MainThread_).\n",
50
+ "All other messages on the shell channel are handled in the control thread.\n",
42
51
  "\n",
43
52
  "## Control messaging\n",
44
53
  "\n",
@@ -401,7 +410,7 @@
401
410
  ],
402
411
  "metadata": {
403
412
  "kernelspec": {
404
- "display_name": "Python (async)",
413
+ "display_name": "Python 3 (async)",
405
414
  "language": "python",
406
415
  "name": "async"
407
416
  },
@@ -415,7 +424,7 @@
415
424
  "name": "python",
416
425
  "nbconvert_exporter": "python",
417
426
  "pygments_lexer": "ipython3",
418
- "version": "3.13.12"
427
+ "version": "3.11.15"
419
428
  }
420
429
  },
421
430
  "nbformat": 4,