async-kernel 0.13.3__tar.gz → 0.15.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 (108) hide show
  1. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/ci.yml +9 -3
  2. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/enforce-label.yml +1 -4
  3. {async_kernel-0.13.3 → async_kernel-0.15.0}/.vscode/launch.json +30 -0
  4. {async_kernel-0.13.3 → async_kernel-0.15.0}/CHANGELOG.md +66 -0
  5. {async_kernel-0.13.3 → async_kernel-0.15.0}/CONTRIBUTING.md +11 -0
  6. {async_kernel-0.13.3 → async_kernel-0.15.0}/PKG-INFO +43 -16
  7. {async_kernel-0.13.3 → async_kernel-0.15.0}/README.md +40 -14
  8. async_kernel-0.15.0/_version.py +24 -0
  9. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/notebooks/caller.ipynb +17 -10
  10. async_kernel-0.15.0/docs/reference/event_loop.md +9 -0
  11. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/index.md +2 -2
  12. async_kernel-0.15.0/docs/thread_safety.md +40 -0
  13. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/usage/commands.md +18 -18
  14. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/usage/index.md +12 -2
  15. {async_kernel-0.13.3 → async_kernel-0.15.0}/mkdocs.yml +3 -3
  16. {async_kernel-0.13.3 → async_kernel-0.15.0}/pyproject.toml +3 -2
  17. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/asyncshell.py +5 -5
  18. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/caller.py +55 -58
  19. async_kernel-0.15.0/src/async_kernel/common.py +132 -0
  20. async_kernel-0.15.0/src/async_kernel/compat/json.py +45 -0
  21. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/debugger.py +3 -3
  22. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/event_loop/asyncio_guest.py +1 -1
  23. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/event_loop/qt_host.py +5 -3
  24. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/event_loop/run.py +27 -28
  25. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/event_loop/tk_host.py +14 -11
  26. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/interface/base.py +19 -4
  27. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/interface/callable.py +7 -53
  28. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/interface/zmq.py +12 -19
  29. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/kernel.py +41 -21
  30. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/pending.py +136 -84
  31. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/typing.py +8 -23
  32. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_callable_kernel_interface.py +11 -7
  33. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_caller.py +8 -11
  34. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_command.py +8 -8
  35. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_common.py +2 -22
  36. async_kernel-0.15.0/tests/test_compat.py +39 -0
  37. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_event_loop.py +84 -8
  38. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_kernel.py +2 -1
  39. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_pending.py +87 -10
  40. {async_kernel-0.13.3 → async_kernel-0.15.0}/uv.lock +336 -359
  41. async_kernel-0.13.3/_version.py +0 -34
  42. async_kernel-0.13.3/docs/reference/event_loop.md +0 -1
  43. async_kernel-0.13.3/src/async_kernel/common.py +0 -138
  44. async_kernel-0.13.3/src/async_kernel/event_loop/asyncio_host.py +0 -41
  45. async_kernel-0.13.3/src/async_kernel/event_loop/trio_host.py +0 -47
  46. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/dependabot.yaml +0 -0
  47. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/release.yml +0 -0
  48. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/new_release.yml +0 -0
  49. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/pre-commit.yml +0 -0
  50. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/publish-docs.yml +0 -0
  51. {async_kernel-0.13.3 → async_kernel-0.15.0}/.github/workflows/publish-to-pypi.yml +0 -0
  52. {async_kernel-0.13.3 → async_kernel-0.15.0}/.gitignore +0 -0
  53. {async_kernel-0.13.3 → async_kernel-0.15.0}/.pre-commit-config.yaml +0 -0
  54. {async_kernel-0.13.3 → async_kernel-0.15.0}/.vscode/settings.json +0 -0
  55. {async_kernel-0.13.3 → async_kernel-0.15.0}/.vscode/spellright.dict +0 -0
  56. {async_kernel-0.13.3 → async_kernel-0.15.0}/IPYTHON_LICENSE +0 -0
  57. {async_kernel-0.13.3 → async_kernel-0.15.0}/LICENSE +0 -0
  58. {async_kernel-0.13.3 → async_kernel-0.15.0}/cliff.toml +0 -0
  59. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/about/changelog.md +0 -0
  60. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/about/contributing.md +0 -0
  61. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/about/index.md +0 -0
  62. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/about/license.md +0 -0
  63. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/index.md +0 -0
  64. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/javascripts/extra.js +0 -0
  65. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/notebooks/concurrency.ipynb +0 -0
  66. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/overrides/main.html +0 -0
  67. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/asyncshell.md +0 -0
  68. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/caller.md +0 -0
  69. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/comm.md +0 -0
  70. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/command.md +0 -0
  71. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/common.md +0 -0
  72. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/debugger.md +0 -0
  73. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/interface.md +0 -0
  74. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/kernel.md +0 -0
  75. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/kernelspec.md +0 -0
  76. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/pending.md +0 -0
  77. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/typing.md +0 -0
  78. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/reference/utils.md +0 -0
  79. {async_kernel-0.13.3 → async_kernel-0.15.0}/docs/stylesheets/extra.css +0 -0
  80. {async_kernel-0.13.3 → async_kernel-0.15.0}/hatch_build.py +0 -0
  81. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/__init__.py +0 -0
  82. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/__main__.py +0 -0
  83. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/comm.py +0 -0
  84. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/command.py +0 -0
  85. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/compiler.py +0 -0
  86. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/event_loop/__init__.py +0 -0
  87. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/interface/__init__.py +0 -0
  88. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/iostream.py +0 -0
  89. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/kernelspec.py +0 -0
  90. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/py.typed +0 -0
  91. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/resources/logo-32x32.png +0 -0
  92. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/resources/logo-64x64.png +0 -0
  93. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/resources/logo-svg.svg +0 -0
  94. {async_kernel-0.13.3 → async_kernel-0.15.0}/src/async_kernel/utils.py +0 -0
  95. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/__init__.py +0 -0
  96. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/conftest.py +0 -0
  97. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/references.py +0 -0
  98. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_comm.py +0 -0
  99. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_debugger.py +0 -0
  100. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_enter_kernel.py +0 -0
  101. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_iostream.py +0 -0
  102. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_kernel_subclass.py +0 -0
  103. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_kernelspec.py +0 -0
  104. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_message_spec.py +0 -0
  105. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_typing.py +0 -0
  106. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_utils.py +0 -0
  107. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/test_zmq_messaging.py +0 -0
  108. {async_kernel-0.13.3 → async_kernel-0.15.0}/tests/utils.py +0 -0
@@ -38,8 +38,7 @@ jobs:
38
38
  - "3.11"
39
39
  - "3.12"
40
40
  - "3.13"
41
- - "3.14"
42
- # - "3.14t" # re-enabled when free-threading becomes more widely supported.
41
+ - "3.14t"
43
42
  steps:
44
43
  - name: Checkout
45
44
  uses: actions/checkout@v6
@@ -54,6 +53,10 @@ jobs:
54
53
  - name: Install the project
55
54
  run: uv sync --locked --dev
56
55
 
56
+ - if: ${{ startsWith(matrix.os, 'windows') && !endsWith(matrix.python-version, 't')}}
57
+ # Pyside does not have free-threaded binary
58
+ run: uv sync --locked --dev --group gui
59
+
57
60
  - name: Run tests
58
61
  timeout-minutes: 5
59
62
  run: uv run pytest -vvl --maxfail=1 --reruns 1
@@ -75,7 +78,10 @@ jobs:
75
78
 
76
79
  - name: Run tests with coverage
77
80
  timeout-minutes: 5
78
- run: uv run pytest --cov --cov-report=xml --junitxml=junit.xml -o junit_family=legacy --cov-fail-under=100 --cov-report markdown-append:$GITHUB_STEP_SUMMARY
81
+ run: |
82
+ uv sync --locked --dev
83
+ uv pip install orjson>=3.10.16
84
+ uv run pytest --cov --cov-report=xml --junitxml=junit.xml -o junit_family=legacy --cov-fail-under=100 --cov-report markdown-append:$GITHUB_STEP_SUMMARY
79
85
 
80
86
  - name: Upload coverage reports to Codecov
81
87
  if: ${{ !cancelled() }}
@@ -1,12 +1,9 @@
1
1
  name: Enforce PR label
2
2
 
3
- concurrency:
4
- group: label-${{ github.ref }}
5
- cancel-in-progress: true
6
-
7
3
  on:
8
4
  pull_request:
9
5
  types: [labeled, unlabeled, opened, edited, synchronize]
6
+
10
7
  jobs:
11
8
  enforce-label:
12
9
  runs-on: ubuntu-latest
@@ -26,6 +26,36 @@
26
26
  },
27
27
  "justMyCode": true,
28
28
  "args": ["-X dev", "Wd"]
29
+ },
30
+ {
31
+ "name": "Jupyterlab",
32
+ "type": "debugpy",
33
+ "request": "launch",
34
+ "args": [
35
+ "--notebook-dir=docs/notebooks",
36
+ "--no-browser",
37
+ "--ServerApp.token=''",
38
+ "--port=9991"
39
+ ],
40
+ "cwd": "${workspaceFolder}",
41
+ "justMyCode": false,
42
+ "module": "jupyterlab",
43
+ "presentation": {
44
+ "hidden": false
45
+ }
46
+ },
47
+ {
48
+ "name": "Editor Browser",
49
+ "type": "editor-browser",
50
+ "request": "launch",
51
+ "url": "http://localhost:9991",
52
+ "webRoot": "${workspaceFolder}"
53
+ }
54
+ ],
55
+ "compounds": [
56
+ {
57
+ "name": "Python and Jupyterlab",
58
+ "configurations": ["Jupyterlab", "Editor Browser"]
29
59
  }
30
60
  ]
31
61
  }
@@ -5,6 +5,68 @@ 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.15.0] - 2026-03-30
9
+
10
+ ### <!-- 0 --> 🏗️ Breaking changes
11
+
12
+ - Provide cancellation in Pending by default [#386](https://github.com/fleming79/async-kernel/pull/386)
13
+
14
+ - Use a standard dict for the user_ns (remove LastUpdatedDict) [#380](https://github.com/fleming79/async-kernel/pull/380)
15
+
16
+ ### <!-- 1 --> 🚀 Features
17
+
18
+ - Replace aiologic.Lock with aiologic.lowlevel.create_thread_oncelock for improved performance [#389](https://github.com/fleming79/async-kernel/pull/389)
19
+
20
+ - Convert Pending._done_callbacks from deque to list [#383](https://github.com/fleming79/async-kernel/pull/383)
21
+
22
+ - Free-threading preliminary support. [#379](https://github.com/fleming79/async-kernel/pull/379)
23
+
24
+ ### <!-- 5 --> 📝 Documentation
25
+
26
+ - Update docstrings [#384](https://github.com/fleming79/async-kernel/pull/384)
27
+
28
+ - Add thread_safety.md [#381](https://github.com/fleming79/async-kernel/pull/381)
29
+
30
+ ### <!-- 6 --> 🌀 Miscellaneous
31
+
32
+ - Change Pending.wait_sync signature to be the same as Pending.wait [#393](https://github.com/fleming79/async-kernel/pull/393)
33
+
34
+ - Tidy up [#391](https://github.com/fleming79/async-kernel/pull/391)
35
+
36
+ - Pending refactoring. [#390](https://github.com/fleming79/async-kernel/pull/390)
37
+
38
+ - Use aiologic.lowlevel.create_async_waiter instead of Event for better performance. [#388](https://github.com/fleming79/async-kernel/pull/388)
39
+
40
+ - Pending refactoring [#385](https://github.com/fleming79/async-kernel/pull/385)
41
+
42
+ - Caller.queue_call optimizations [#382](https://github.com/fleming79/async-kernel/pull/382)
43
+
44
+ - Add Kernel.run and refactor the interfaces. [#378](https://github.com/fleming79/async-kernel/pull/378)
45
+
46
+ - Use Kernel._get_run_mode for all message types. [#377](https://github.com/fleming79/async-kernel/pull/377)
47
+
48
+ - Add a VS Code launch config for to launch Jupyterlab [#376](https://github.com/fleming79/async-kernel/pull/376)
49
+
50
+ - Upgrade uv.lock and run gui tests using windows. [#375](https://github.com/fleming79/async-kernel/pull/375)
51
+
52
+ - Update uv.lock [#374](https://github.com/fleming79/async-kernel/pull/374)
53
+
54
+ ## [0.14.0] - 2026-03-24
55
+
56
+ ### <!-- 0 --> 🏗️ Breaking changes
57
+
58
+ - Rename loop to host and Loop to Hosts [#370](https://github.com/fleming79/async-kernel/pull/370)
59
+
60
+ ### <!-- 5 --> 📝 Documentation
61
+
62
+ - Update event loop documentation. [#372](https://github.com/fleming79/async-kernel/pull/372)
63
+
64
+ - Update readme [#371](https://github.com/fleming79/async-kernel/pull/371)
65
+
66
+ ### <!-- 6 --> 🌀 Miscellaneous
67
+
68
+ - Prepare for release v0.14.0 [#373](https://github.com/fleming79/async-kernel/pull/373)
69
+
8
70
  ## [0.13.3] - 2026-03-23
9
71
 
10
72
  ### <!-- 0 --> 🏗️ Breaking changes
@@ -15,6 +77,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
77
 
16
78
  ### <!-- 6 --> 🌀 Miscellaneous
17
79
 
80
+ - Prepare for release v0.13.3 [#369](https://github.com/fleming79/async-kernel/pull/369)
81
+
18
82
  - Minor SingleConsumerAsyncQueue optimization. [#368](https://github.com/fleming79/async-kernel/pull/368)
19
83
 
20
84
  - Fixtest_command_start_kernel_enable_matplotlib [#367](https://github.com/fleming79/async-kernel/pull/367)
@@ -1023,6 +1087,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1023
1087
 
1024
1088
  - Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
1025
1089
 
1090
+ [0.15.0]: https://github.com/fleming79/async-kernel/compare/v0.14.0..v0.15.0
1091
+ [0.14.0]: https://github.com/fleming79/async-kernel/compare/v0.13.3..v0.14.0
1026
1092
  [0.13.3]: https://github.com/fleming79/async-kernel/compare/v0.13.2..v0.13.3
1027
1093
  [0.13.2]: https://github.com/fleming79/async-kernel/compare/v0.13.1..v0.13.2
1028
1094
  [0.13.1]: https://github.com/fleming79/async-kernel/compare/v0.13.0..v0.13.1
@@ -208,3 +208,14 @@ uv run mkdocs build -s
208
208
  tests can still fail for another os or python version.
209
209
 
210
210
  [^test-pypi]: This workflow also runs on push to the main branch, but will instead publish to [TestPyPI](https://test.pypi.org/project/async-kernel/).
211
+
212
+ ## VS code development
213
+
214
+ Debug configurations are included to support debugging with VS Code.
215
+
216
+ - Python: Debug Tests: This config is for attaching a debugger when launching tests.
217
+ The recommended way to debug tests.
218
+ - Python and Jupyterlab (Requires VS Code >1.112.0): This provides a compound launch configuration
219
+ which starts Jupyterlab and Python. The built in browser launches attaches to the Jupyter session
220
+ (refresh may be required after the server has started). The advantage of this configuration
221
+ is that all Python processes are attached to the debugger whenever a kernel is opened.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async-kernel
3
- Version: 0.13.3
3
+ Version: 0.15.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
@@ -29,6 +29,7 @@ Classifier: Programming Language :: Python :: 3.11
29
29
  Classifier: Programming Language :: Python :: 3.12
30
30
  Classifier: Programming Language :: Python :: 3.13
31
31
  Classifier: Programming Language :: Python :: 3.14
32
+ Classifier: Programming Language :: Python :: Free Threading :: 1 - Unstable
32
33
  Classifier: Typing :: Typed
33
34
  Requires-Python: >=3.11
34
35
  Requires-Dist: aiologic>=0.16.0
@@ -38,7 +39,7 @@ Requires-Dist: ipython>=9.0
38
39
  Requires-Dist: jupyter-client>=8.8; sys_platform != 'emscripten'
39
40
  Requires-Dist: jupyter-core>=5.9.1
40
41
  Requires-Dist: matplotlib-inline>0.1
41
- Requires-Dist: orjson>=3.10.16
42
+ Requires-Dist: orjson>=3.10.16; sys_platform == 'emscripten'
42
43
  Requires-Dist: outcome; sys_platform != 'emscripten'
43
44
  Requires-Dist: pyzmq>=27.0; sys_platform != 'emscripten'
44
45
  Requires-Dist: sniffio>=1.3.0; sys_platform != 'emscripten'
@@ -65,12 +66,13 @@ that provides concurrent message handling via an asynchronous backend (asyncio o
65
66
 
66
67
  The kernel provides two external interfaces:
67
68
 
68
- 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VScode, etc).
69
+ 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VS Code, etc).
69
70
  2. An experimental callback style interface (Jupyterlite).
70
71
 
71
72
  ## Highlights
72
73
 
73
74
  - [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
75
+ - top-level await ('asyncio' or 'trio' backend) in cells
74
76
  - [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
75
77
  - [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
76
78
  - [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
@@ -85,9 +87,8 @@ The kernel provides two external interfaces:
85
87
  - [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
86
88
 
87
89
  [^1]:
88
- A gui (_host_) enabled kernel runs a gui event loop with the asynchronous backend
89
- running as guest. The host must be set before the kernel is started. This was a
90
- deliberate design choice to to ensure good performance and reliability.
90
+ A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
91
+ the backend as a guest, then finally the Kernel is started.
91
92
 
92
93
  [^2]:
93
94
  The asyncio implementation of `start_guest_run` was written by
@@ -105,24 +106,40 @@ Another problem exists when an asynchronous execute request awaits a result that
105
106
  via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
106
107
  the _blocking_ execute request[^5].
107
108
 
108
- async-kernel handles messages according to the channel, message type and subshell id. So widget com message
109
+ async-kernel handles messages according to the channel and message type. So widget com message
109
110
  will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
110
111
 
111
112
  #### Example
112
113
 
113
- Try the following using a standard kernel and then try it with async-kernel.
114
+ Make a blocking call in a Jupyter lab notebook or console.
114
115
 
115
116
  ```python
116
- # Make the shell thread busy
117
+ # Make the shell's thread busy
117
118
  import time
118
119
 
119
120
  time.sleep(1e6)
120
121
  ```
121
122
 
122
- Try the following in another cell:
123
+ While the above is _blocking_ (the kernel is _busy_).
123
124
 
124
- - code completion (`tab`)
125
- - docstring (`shift tab`)
125
+ ```python
126
+ dir() # try code completion (tab) or view the docstring (shift tab)
127
+ ```
128
+
129
+ Interrupt the kernel.
130
+
131
+ It also works for awaitables.
132
+
133
+ ```python
134
+ import ipywidgets as ipw
135
+ from aiologic import Event
136
+
137
+ b = ipw.Button(description="Click me")
138
+ event = Event()
139
+ b.on_click(lambda _: event.set())
140
+ display(b)
141
+ await event
142
+ ```
126
143
 
127
144
  [^5]:
128
145
  IPyKernel _solves_ this issue specifically for widgets by using the concept of
@@ -165,11 +182,11 @@ The kernel can be started with a gui event loop as the _host_ and the _backend_
165
182
 
166
183
  ```bash
167
184
  # tk
168
- async-kernel -a async-tk --interface.loop=tk
185
+ async-kernel -a async-tk --interface.host=tk
169
186
 
170
187
  # qt
171
188
  pip install PySide6-Essentials
172
- async-kernel -a async-qt --interface.loop=qt
189
+ async-kernel -a async-qt --interface.host=qt
173
190
  ```
174
191
 
175
192
  #### trio backend
@@ -177,15 +194,25 @@ async-kernel -a async-qt --interface.loop=qt
177
194
  ```bash
178
195
  pip install trio
179
196
  # tk
180
- async-kernel -a async-tk --interface.loop=tk --interface.backend=trio
197
+ async-kernel -a async-tk --interface.host=tk --interface.backend=trio
181
198
 
182
199
  # qt
183
200
  pip install PySide6-Essentials
184
- async-kernel -a async-qt --interface.loop=qt --interface.backend=trio
201
+ async-kernel -a async-qt --interface.host=qt --interface.backend=trio
185
202
  ```
186
203
 
187
204
  For further detail about kernel spec customisation see [command line and kernel configuration](https://fleming79.github.io/async-kernel/latest/usage/commands/).
188
205
 
206
+ ## Faster data serialization
207
+
208
+ [orjson](https://github.com/ijl/orjson) (a fast JSON library) is supported and will be used by default if it has been installed.
209
+
210
+ ## Free-threading support
211
+
212
+ async-kernel's Caller's are _thread-local_ and it's methods are _internally synchronised_[^4].
213
+
214
+ [^4]: [free threading terminology](https://py-free-threading.github.io/documentation-principles/#free-threading-terminology)
215
+
189
216
  ## Origin
190
217
 
191
218
  async-kernel started as a [fork](https://github.com/ipython/ipykernel/commit/8322a7684b004ee95f07b2f86f61e28146a5996d)
@@ -16,12 +16,13 @@ that provides concurrent message handling via an asynchronous backend (asyncio o
16
16
 
17
17
  The kernel provides two external interfaces:
18
18
 
19
- 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VScode, etc).
19
+ 1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VS Code, etc).
20
20
  2. An experimental callback style interface (Jupyterlite).
21
21
 
22
22
  ## Highlights
23
23
 
24
24
  - [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
25
+ - top-level await ('asyncio' or 'trio' backend) in cells
25
26
  - [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
26
27
  - [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
27
28
  - [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
@@ -36,9 +37,8 @@ The kernel provides two external interfaces:
36
37
  - [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)
37
38
 
38
39
  [^1]:
39
- A gui (_host_) enabled kernel runs a gui event loop with the asynchronous backend
40
- running as guest. The host must be set before the kernel is started. This was a
41
- deliberate design choice to to ensure good performance and reliability.
40
+ A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
41
+ the backend as a guest, then finally the Kernel is started.
42
42
 
43
43
  [^2]:
44
44
  The asyncio implementation of `start_guest_run` was written by
@@ -56,24 +56,40 @@ Another problem exists when an asynchronous execute request awaits a result that
56
56
  via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
57
57
  the _blocking_ execute request[^5].
58
58
 
59
- async-kernel handles messages according to the channel, message type and subshell id. So widget com message
59
+ async-kernel handles messages according to the channel and message type. So widget com message
60
60
  will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).
61
61
 
62
62
  #### Example
63
63
 
64
- Try the following using a standard kernel and then try it with async-kernel.
64
+ Make a blocking call in a Jupyter lab notebook or console.
65
65
 
66
66
  ```python
67
- # Make the shell thread busy
67
+ # Make the shell's thread busy
68
68
  import time
69
69
 
70
70
  time.sleep(1e6)
71
71
  ```
72
72
 
73
- Try the following in another cell:
73
+ While the above is _blocking_ (the kernel is _busy_).
74
74
 
75
- - code completion (`tab`)
76
- - docstring (`shift tab`)
75
+ ```python
76
+ dir() # try code completion (tab) or view the docstring (shift tab)
77
+ ```
78
+
79
+ Interrupt the kernel.
80
+
81
+ It also works for awaitables.
82
+
83
+ ```python
84
+ import ipywidgets as ipw
85
+ from aiologic import Event
86
+
87
+ b = ipw.Button(description="Click me")
88
+ event = Event()
89
+ b.on_click(lambda _: event.set())
90
+ display(b)
91
+ await event
92
+ ```
77
93
 
78
94
  [^5]:
79
95
  IPyKernel _solves_ this issue specifically for widgets by using the concept of
@@ -116,11 +132,11 @@ The kernel can be started with a gui event loop as the _host_ and the _backend_
116
132
 
117
133
  ```bash
118
134
  # tk
119
- async-kernel -a async-tk --interface.loop=tk
135
+ async-kernel -a async-tk --interface.host=tk
120
136
 
121
137
  # qt
122
138
  pip install PySide6-Essentials
123
- async-kernel -a async-qt --interface.loop=qt
139
+ async-kernel -a async-qt --interface.host=qt
124
140
  ```
125
141
 
126
142
  #### trio backend
@@ -128,15 +144,25 @@ async-kernel -a async-qt --interface.loop=qt
128
144
  ```bash
129
145
  pip install trio
130
146
  # tk
131
- async-kernel -a async-tk --interface.loop=tk --interface.backend=trio
147
+ async-kernel -a async-tk --interface.host=tk --interface.backend=trio
132
148
 
133
149
  # qt
134
150
  pip install PySide6-Essentials
135
- async-kernel -a async-qt --interface.loop=qt --interface.backend=trio
151
+ async-kernel -a async-qt --interface.host=qt --interface.backend=trio
136
152
  ```
137
153
 
138
154
  For further detail about kernel spec customisation see [command line and kernel configuration](https://fleming79.github.io/async-kernel/latest/usage/commands/).
139
155
 
156
+ ## Faster data serialization
157
+
158
+ [orjson](https://github.com/ijl/orjson) (a fast JSON library) is supported and will be used by default if it has been installed.
159
+
160
+ ## Free-threading support
161
+
162
+ async-kernel's Caller's are _thread-local_ and it's methods are _internally synchronised_[^4].
163
+
164
+ [^4]: [free threading terminology](https://py-free-threading.github.io/documentation-principles/#free-threading-terminology)
165
+
140
166
  ## Origin
141
167
 
142
168
  async-kernel started as a [fork](https://github.com/ipython/ipykernel/commit/8322a7684b004ee95f07b2f86f61e28146a5996d)
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.15.0'
22
+ __version_tuple__ = version_tuple = (0, 15, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -15,7 +15,7 @@
15
15
  "\n",
16
16
  "_This notebook has the Python dependencies: `trio`, `matplotlib`_\n",
17
17
  "\n",
18
- "`Caller` is a class that enables standard functions and coroutines to be called in the event loop of a `asyncio` or `trio` backend. Scheduling is thread-safe, methods that return `Pending` are thread-safe cancellable and awaitable."
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."
19
19
  ]
20
20
  },
21
21
  {
@@ -35,6 +35,7 @@
35
35
  "metadata": {},
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
+ "\n",
38
39
  "A second backend can also be started in the same thread with or without a host gui event loop.\n",
39
40
  "Potentially the same thread could have up to:\n",
40
41
  "\n",
@@ -77,11 +78,10 @@
77
78
  "id": "5",
78
79
  "metadata": {},
79
80
  "source": [
80
- "There are three caller instances associated with the Kernel:\n",
81
+ "There are two caller instances associated with the Kernel:\n",
81
82
  "\n",
82
83
  "- Shell: The caller for the shell channel (normally the MainThread).\n",
83
- "- Control: The caller for the control channel (a child of the shell).\n",
84
- "- Shell thread_queue: A separate thread to handle shell messages in a separate thread (queued by message type and subshell_id)."
84
+ "- Control: The caller for the control channel (a child of the shell)."
85
85
  ]
86
86
  },
87
87
  {
@@ -89,7 +89,9 @@
89
89
  "id": "6",
90
90
  "metadata": {},
91
91
  "source": [
92
- "Calling `Caller()` with no arguments will return the caller for the current thread (context in Pyodide). A runtime error will occur if when there is no backend running in the current thread (in Pyodide the main caller is always available)."
92
+ "Calling `Caller()` with no arguments will return the caller for the current thread (or context in Pyodide). \n",
93
+ "\n",
94
+ "A runtime error will occur if there is no event loop (backend) running in the current thread (unlikely in Pyodide which provides a running event loop)."
93
95
  ]
94
96
  },
95
97
  {
@@ -111,7 +113,7 @@
111
113
  "source": [
112
114
  "## Modifiers\n",
113
115
  "\n",
114
- "There are four modifiers that determine the instance returned when calling `Caller`.\n",
116
+ "There are four modifiers that determine which instance is returned when calling `Caller`.\n",
115
117
  "\n",
116
118
  "- `CurrentThread` (default): Access the caller that belongs to the current thread (or context with Pyodide).\n",
117
119
  "- `MainThread`: Access main thread for the Caller.\n",
@@ -285,7 +287,9 @@
285
287
  "source": [
286
288
  "### Caller.call_using_backend\n",
287
289
  "\n",
288
- "This method calls functions and coroutines using a specific asynchronous backend in the same thread. When the backend does not match the callers backend, it is run with the backend running as a guest."
290
+ "This method calls functions and coroutines using a specific asynchronous backend in the same thread. \n",
291
+ "\n",
292
+ "When the backend does not match the callers backend, it is run with the backend running as a guest."
289
293
  ]
290
294
  },
291
295
  {
@@ -490,10 +494,13 @@
490
494
  "metadata": {},
491
495
  "outputs": [],
492
496
  "source": [
497
+ "import anyio\n",
498
+ "\n",
493
499
  "async with caller.create_pending_group() as pg:\n",
494
- " caller.call_soon(asyncio.sleep, 0.01)\n",
495
- " caller.call_later(0, asyncio.sleep, 0)\n",
496
- " caller.to_thread(asyncio.sleep, 0.01)\n",
500
+ " caller.call_soon(anyio.sleep, 0.01)\n",
501
+ " caller.call_later(0, anyio.sleep, 0)\n",
502
+ " caller.to_thread(anyio.sleep, 0.01)\n",
503
+ " caller.call_using_backend(\"asyncio\", asyncio.sleep, 0.01)\n",
497
504
  " caller.call_using_backend(\"trio\", trio.sleep, 0.01)\n",
498
505
  " print(\"In context pending=\", len(pg.pending))\n",
499
506
  "\n",
@@ -0,0 +1,9 @@
1
+ ::: async_kernel.event_loop.run
2
+
3
+ ::: async_kernel.event_loop.tk_host.TkHost
4
+ options:
5
+ show_root_heading: true
6
+
7
+ ::: async_kernel.event_loop.qt_host.QtHost
8
+ options:
9
+ show_root_heading: true
@@ -12,6 +12,6 @@ The reference section provides documentation for each module in async-kernel.
12
12
  ## Highlights
13
13
 
14
14
  - [Kernel][async_kernel.kernel.Kernel] - The kernel.
15
- - [Caller][async_kernel.caller.Caller] - Simplifies event loop management and call scheduling (thread-safe).
16
- - [Pending][async_kernel.pending.Pending] - Represents a pending result that can be set, awaited and cancelled (thread-safe).
15
+ - [Caller][async_kernel.caller.Caller] - Simplifies event loop management and call scheduling.
16
+ - [Pending][async_kernel.pending.Pending] - Represents a pending result (like an [asyncio.Future][] that can be awaited anywhere).
17
17
  - [command_line][async_kernel.command.command_line] - The command line interface.
@@ -0,0 +1,40 @@
1
+ # Thread Safety of Built-in Types
2
+
3
+ - [Terminology](https://py-free-threading.github.io/documentation-principles/#free-threading-terminology)
4
+
5
+ _aync-kernel_ utilises [aiologic](https://aiologic.readthedocs.io/latest/index.html) for its _thread-safe_ primitives.
6
+
7
+ ## [async_kernel.pending.Pending][]
8
+
9
+ ### Guarantees
10
+
11
+ - **Internally synchronised**
12
+ - All Public methods can be safely called from any thread.
13
+ - Instance are sync/async awaitable from any thread with any backend.
14
+
15
+ - **Limitations**
16
+
17
+ [add_done_callback][async_kernel.pending.Pending.add_done_callback]: The callback is called from the thread in which it is set
18
+ so should provide it's own synchronisation as needed.
19
+
20
+ ## [async_kernel.caller.Caller][]
21
+
22
+ `Caller` is thread-local in CPython and context-local in Pyodide.
23
+
24
+ ### Guarantees
25
+
26
+ - **Internally synchronised**
27
+
28
+ - **Limitations**
29
+ - [queue_call][async_kernel.caller.Caller.queue_call] is internally synchronised per-function after
30
+ the first call (per-function) has returned.
31
+
32
+ ## [async_kernel.common.Fixed][]
33
+
34
+ ### Guarantees
35
+
36
+ - **Internally synchronised**
37
+
38
+ ### Other classes
39
+
40
+ TODO