async-kernel 0.17.0__tar.gz → 0.17.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 (104) hide show
  1. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/ci.yml +2 -2
  2. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/publish-docs.yml +1 -1
  3. {async_kernel-0.17.0 → async_kernel-0.17.1}/.pre-commit-config.yaml +4 -4
  4. {async_kernel-0.17.0 → async_kernel-0.17.1}/CHANGELOG.md +24 -1
  5. {async_kernel-0.17.0 → async_kernel-0.17.1}/CONTRIBUTING.md +4 -8
  6. {async_kernel-0.17.0 → async_kernel-0.17.1}/PKG-INFO +1 -1
  7. {async_kernel-0.17.0 → async_kernel-0.17.1}/_version.py +2 -2
  8. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/notebooks/caller.ipynb +1 -1
  9. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/notebooks/concurrency.ipynb +157 -7
  10. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/asyncshell.py +59 -23
  11. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/caller.py +1 -2
  12. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/interface/base.py +150 -159
  13. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/interface/callable.py +2 -2
  14. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/interface/zmq.py +2 -2
  15. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/typing.py +42 -24
  16. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_kernel.py +35 -32
  17. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_typing.py +3 -3
  18. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_utils.py +3 -1
  19. {async_kernel-0.17.0 → async_kernel-0.17.1}/uv.lock +75 -80
  20. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/dependabot.yaml +0 -0
  21. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/release.yml +0 -0
  22. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/enforce-label.yml +0 -0
  23. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/new_release.yml +0 -0
  24. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/pre-commit.yml +0 -0
  25. {async_kernel-0.17.0 → async_kernel-0.17.1}/.github/workflows/publish-to-pypi.yml +0 -0
  26. {async_kernel-0.17.0 → async_kernel-0.17.1}/.gitignore +0 -0
  27. {async_kernel-0.17.0 → async_kernel-0.17.1}/.vscode/launch.json +0 -0
  28. {async_kernel-0.17.0 → async_kernel-0.17.1}/.vscode/settings.json +0 -0
  29. {async_kernel-0.17.0 → async_kernel-0.17.1}/.vscode/spellright.dict +0 -0
  30. {async_kernel-0.17.0 → async_kernel-0.17.1}/IPYTHON_LICENSE +0 -0
  31. {async_kernel-0.17.0 → async_kernel-0.17.1}/LICENSE +0 -0
  32. {async_kernel-0.17.0 → async_kernel-0.17.1}/README.md +0 -0
  33. {async_kernel-0.17.0 → async_kernel-0.17.1}/cliff.toml +0 -0
  34. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/about/changelog.md +0 -0
  35. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/about/contributing.md +0 -0
  36. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/about/index.md +0 -0
  37. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/about/license.md +0 -0
  38. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/index.md +0 -0
  39. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/javascripts/extra.js +0 -0
  40. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/notebooks/custom_kernel.ipynb +0 -0
  41. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/overrides/main.html +0 -0
  42. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/asyncshell.md +0 -0
  43. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/caller.md +0 -0
  44. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/comm.md +0 -0
  45. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/command.md +0 -0
  46. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/common.md +0 -0
  47. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/debugger.md +0 -0
  48. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/event_loop.md +0 -0
  49. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/index.md +0 -0
  50. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/interface.md +0 -0
  51. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/kernel.md +0 -0
  52. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/kernelspec.md +0 -0
  53. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/pending.md +0 -0
  54. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/typing.md +0 -0
  55. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/reference/utils.md +0 -0
  56. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/stylesheets/extra.css +0 -0
  57. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/thread_safety.md +0 -0
  58. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/usage/commands.md +0 -0
  59. {async_kernel-0.17.0 → async_kernel-0.17.1}/docs/usage/index.md +0 -0
  60. {async_kernel-0.17.0 → async_kernel-0.17.1}/hatch_build.py +0 -0
  61. {async_kernel-0.17.0 → async_kernel-0.17.1}/mkdocs.yml +0 -0
  62. {async_kernel-0.17.0 → async_kernel-0.17.1}/pyproject.toml +0 -0
  63. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/__init__.py +0 -0
  64. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/__main__.py +0 -0
  65. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/comm.py +0 -0
  66. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/command.py +0 -0
  67. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/common.py +0 -0
  68. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/compat/json.py +0 -0
  69. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/compiler.py +0 -0
  70. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/debugger.py +0 -0
  71. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/event_loop/__init__.py +0 -0
  72. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
  73. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/event_loop/qt_host.py +0 -0
  74. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/event_loop/run.py +0 -0
  75. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/event_loop/tk_host.py +0 -0
  76. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/interface/__init__.py +0 -0
  77. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/iostream.py +0 -0
  78. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/kernel.py +0 -0
  79. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/kernelspec.py +0 -0
  80. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/pending.py +0 -0
  81. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/py.typed +0 -0
  82. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/resources/logo-32x32.png +0 -0
  83. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/resources/logo-64x64.png +0 -0
  84. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/resources/logo-svg.svg +0 -0
  85. {async_kernel-0.17.0 → async_kernel-0.17.1}/src/async_kernel/utils.py +0 -0
  86. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/__init__.py +0 -0
  87. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/conftest.py +0 -0
  88. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/references.py +0 -0
  89. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_callable_kernel_interface.py +0 -0
  90. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_caller.py +0 -0
  91. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_comm.py +0 -0
  92. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_command.py +0 -0
  93. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_common.py +0 -0
  94. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_compat.py +0 -0
  95. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_debugger.py +0 -0
  96. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_enter_kernel.py +0 -0
  97. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_event_loop.py +0 -0
  98. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_iostream.py +0 -0
  99. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_kernel_subclass.py +0 -0
  100. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_kernelspec.py +0 -0
  101. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_message_spec.py +0 -0
  102. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_pending.py +0 -0
  103. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/test_zmq_messaging.py +0 -0
  104. {async_kernel-0.17.0 → async_kernel-0.17.1}/tests/utils.py +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Type checking with basedpyright
26
26
  run: |
27
27
  uv sync
28
- uvx basedpyright@1.39.2
28
+ uvx basedpyright@1.39.3
29
29
 
30
30
  test:
31
31
  needs: type-checking
@@ -110,5 +110,5 @@ jobs:
110
110
  timeout-minutes: 5
111
111
  run: |
112
112
  uv sync --group docs
113
- uv run async-kernel -a async-docs --shell.timeout=0.1
113
+ uv run async-kernel -a async-docs --shell.timeout=1
114
114
  uv run mkdocs build -s
@@ -40,7 +40,7 @@ jobs:
40
40
  - name: Install the project
41
41
  run: |
42
42
  uv sync --group docs
43
- uv run async-kernel -a async-docs --shell.timeout=0.1 # The 'async-docs' kernel is specified as the kernel for mkdocs-jupyter
43
+ uv run async-kernel -a async-docs --shell.timeout=1 # The 'async-docs' kernel is specified as the kernel for mkdocs-jupyter
44
44
 
45
45
  - name: Version info
46
46
  id: version
@@ -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.0
31
+ rev: 0.37.1
32
32
  hooks:
33
33
  - id: check-github-workflows
34
34
 
35
35
  - repo: https://github.com/rbubley/mirrors-prettier
36
- rev: v3.8.1
36
+ rev: v3.8.3
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.6
62
+ rev: v0.15.11
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.03.02
71
+ rev: 2026.04.04
72
72
  hooks:
73
73
  - id: sp-repo-review
74
74
  additional_dependencies: ["repo-review[cli]"]
@@ -5,7 +5,25 @@ 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.17.0] - 2026-04-20
8
+ ## [0.17.1] - 2026-04-23
9
+
10
+ ### <!-- 0 --> 🏗️ Breaking changes
11
+
12
+ - Drop the tag 'suppress_error'. [#436](https://github.com/fleming79/async-kernel/pull/436)
13
+
14
+ - BaseKernelInterface refactoring [#434](https://github.com/fleming79/async-kernel/pull/434)
15
+
16
+ ### <!-- 1 --> 🚀 Features
17
+
18
+ - Add asyncio and trio cell and line magic [#437](https://github.com/fleming79/async-kernel/pull/437)
19
+
20
+ - Support specifing the thread with cell magic and passing thread options [#435](https://github.com/fleming79/async-kernel/pull/435)
21
+
22
+ ### <!-- 6 --> 🌀 Miscellaneous
23
+
24
+ - Update pre-commit, uv.lock and basedpyright [#438](https://github.com/fleming79/async-kernel/pull/438)
25
+
26
+ ## [0.17.0] - 2026-04-22
9
27
 
10
28
  ### <!-- 0 --> 🏗️ Breaking changes
11
29
 
@@ -15,6 +33,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
33
 
16
34
  - Make AsyncInteractiveShell and AsyncInteractiveSubshell easier to subclass. [#430](https://github.com/fleming79/async-kernel/pull/430)
17
35
 
36
+ ### <!-- 6 --> 🌀 Miscellaneous
37
+
38
+ - Prepare for release v0.17.0 [#433](https://github.com/fleming79/async-kernel/pull/433)
39
+
18
40
  ## [0.16.4] - 2026-04-15
19
41
 
20
42
  ### <!-- 2 --> 🐛 Fixes
@@ -1197,6 +1219,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1197
1219
 
1198
1220
  - Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
1199
1221
 
1222
+ [0.17.1]: https://github.com/fleming79/async-kernel/compare/v0.17.0..v0.17.1
1200
1223
  [0.17.0]: https://github.com/fleming79/async-kernel/compare/v0.16.4..v0.17.0
1201
1224
  [0.16.4]: https://github.com/fleming79/async-kernel/compare/v0.16.3..v0.16.4
1202
1225
  [0.16.3]: https://github.com/fleming79/async-kernel/compare/v0.16.2..v0.16.3
@@ -25,7 +25,7 @@ Additional steps to build documentation (optional):
25
25
 
26
26
  ```bash
27
27
  uv sync --group docs
28
- uv run async-kernel -a async-docs --main_shell.timeout=0.1
28
+ uv run async-kernel -a async-docs --main_shell.timeout=1
29
29
  ```
30
30
 
31
31
  ### Running tests
@@ -100,7 +100,7 @@ The 'docs' group specified extra packages are required to build documentation.
100
100
 
101
101
  ```bash
102
102
  uv sync --group docs
103
- uv run async-kernel -a async-docs --main_shell.timeout=0.1
103
+ uv run async-kernel -a async-docs --main_shell.timeout=1
104
104
  ```
105
105
 
106
106
  #### Test the docs
@@ -114,7 +114,7 @@ uv run mkdocs build -s
114
114
  The command:
115
115
 
116
116
  ```bash
117
- uv run async-kernel -a async-docs --main_shell.timeout=0.1
117
+ uv run async-kernel -a async-docs --main_shell.timeout=1
118
118
  ```
119
119
 
120
120
  Defines a new kernel spec with the name "async-docs" that sets the `shell.timeout` to 100ms.
@@ -145,13 +145,9 @@ Notebooks are included in the documentation by the plugin [mkdocs-jupyter](https
145
145
 
146
146
  !!! info
147
147
 
148
- We use the kernel spec named 'async-docs' which has a cell execute timeout of 100ms. This is used
148
+ We use the kernel spec named 'async-docs' which has a cell execute timeout of 1s. This is used
149
149
  to advance execution through long running cells.
150
150
 
151
- The [suppress-error][async_kernel.typing.Tags.suppress_error] tag is inserted in code cells to enable
152
- with generating documentation. The symbol '⚠' is an indicator that the error was suppressed. Normally
153
- this is due to the timeout but there is no distinction on the type of error.
154
-
155
151
  #### Useful links
156
152
 
157
153
  These links are not relevant for docstrings.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async-kernel
3
- Version: 0.17.0
3
+ Version: 0.17.1
4
4
  Summary: A concurrent python kernel for Jupyter supporting AnyIO, AsyncIO and Trio.
5
5
  Project-URL: Homepage, https://fleming79.github.io/async-kernel
6
6
  Project-URL: Documentation, https://fleming79.github.io/async-kernel
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.17.0'
22
- __version_tuple__ = version_tuple = (0, 17, 0)
21
+ __version__ = version = '0.17.1'
22
+ __version_tuple__ = version_tuple = (0, 17, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -233,7 +233,7 @@
233
233
  "source": [
234
234
  "## Caller methods that return Pending\n",
235
235
  "\n",
236
- "Pending is like a thread-safe [asyncio.Future](https://docs.python.org/3/library/asyncio-future.html#asyncio.Future) like object to return future results. It was called `Pending` to avoid confusion about differences in functionality to that of `asyncio.Future` and `concurrent.futures.Future`.\n",
236
+ "Pending is a thread-safe representation of a future result. It was designed to provide thread-safe functionality similar [asyncio.Future](https://docs.python.org/3/library/asyncio-future.html#asyncio.Future) and was named `Pending` to avoid confusion about differences in functionality to that of `asyncio.Future` and `concurrent.futures.Future`.\n",
237
237
  "\n",
238
238
  "The following functions return a `Pending` object:\n",
239
239
  "\n",
@@ -187,10 +187,7 @@
187
187
  "editable": true,
188
188
  "slideshow": {
189
189
  "slide_type": ""
190
- },
191
- "tags": [
192
- "suppress-error"
193
- ]
190
+ }
194
191
  },
195
192
  "outputs": [],
196
193
  "source": [
@@ -223,13 +220,12 @@
223
220
  "slide_type": ""
224
221
  },
225
222
  "tags": [
226
- "thread",
227
- "suppress-error"
223
+ "thread"
228
224
  ]
229
225
  },
230
226
  "outputs": [],
231
227
  "source": [
232
- "# This time we'll use the tag to run the cell in a Thread\n",
228
+ "# This time we'll use the tag to run the cell in a worker thread\n",
233
229
  "await demo()"
234
230
  ]
235
231
  },
@@ -249,6 +245,160 @@
249
245
  "# thread\n",
250
246
  "%callers # magic provided by async-kernel"
251
247
  ]
248
+ },
249
+ {
250
+ "cell_type": "markdown",
251
+ "id": "11",
252
+ "metadata": {},
253
+ "source": [
254
+ "We can also specify CallerCreateOptions as part of the top line"
255
+ ]
256
+ },
257
+ {
258
+ "cell_type": "code",
259
+ "execution_count": null,
260
+ "id": "12",
261
+ "metadata": {},
262
+ "outputs": [],
263
+ "source": [
264
+ "# thread name=\"My thread\"\n",
265
+ "%callers"
266
+ ]
267
+ },
268
+ {
269
+ "cell_type": "markdown",
270
+ "id": "13",
271
+ "metadata": {},
272
+ "source": [
273
+ "## Asynchronous magic\n",
274
+ "\n",
275
+ "Asynchronous line (%) and cell (%%) magic functions are supported. Any line or cell magic that returns an awaitable is awaited before proceeding.\n",
276
+ "\n",
277
+ "- **[thread magic](#thread-magic)**\n",
278
+ "- **[asyncio magic](#specify-the-backend)**\n",
279
+ "- **[trio magic](#specify-the-backend)**\n",
280
+ "\n",
281
+ "### thread magic\n",
282
+ "\n",
283
+ "This will run the code in a thread. When no settings are provided a cell worker thread is used.\n",
284
+ "\n",
285
+ "#### Comparing thread magic with thread run mode\n",
286
+ "\n",
287
+ "- thread magic (`%%thread`) is an asynchronous magic that executes the associated **code** in a separate thread. \n",
288
+ "- [thread run mode](#run-mode-thread) (`# thread`) instructs the kernel to run the **entire cell** in a separate thread, bypassing the shell execute request queue."
289
+ ]
290
+ },
291
+ {
292
+ "cell_type": "code",
293
+ "execution_count": null,
294
+ "id": "14",
295
+ "metadata": {},
296
+ "outputs": [],
297
+ "source": [
298
+ "# Run the magic 'callers' in a caller worker thread.\n",
299
+ "%thread %callers "
300
+ ]
301
+ },
302
+ {
303
+ "cell_type": "markdown",
304
+ "id": "15",
305
+ "metadata": {},
306
+ "source": [
307
+ "To specify a thread (caller) by name"
308
+ ]
309
+ },
310
+ {
311
+ "cell_type": "code",
312
+ "execution_count": null,
313
+ "id": "16",
314
+ "metadata": {},
315
+ "outputs": [],
316
+ "source": [
317
+ "%%thread name=\"My executor\" \n",
318
+ "%callers"
319
+ ]
320
+ },
321
+ {
322
+ "cell_type": "markdown",
323
+ "id": "17",
324
+ "metadata": {},
325
+ "source": [
326
+ "Many of arguments accepted on `Caller.get` are also supported. Let's use a thread with a trio backend."
327
+ ]
328
+ },
329
+ {
330
+ "cell_type": "code",
331
+ "execution_count": null,
332
+ "id": "18",
333
+ "metadata": {},
334
+ "outputs": [],
335
+ "source": [
336
+ "%%thread name=\"My trio executor\" backend=trio\n",
337
+ "%callers\n",
338
+ "\n",
339
+ "import trio\n",
340
+ "\n",
341
+ "await trio.sleep(0)"
342
+ ]
343
+ },
344
+ {
345
+ "cell_type": "markdown",
346
+ "id": "19",
347
+ "metadata": {},
348
+ "source": [
349
+ "## Specify the backend\n",
350
+ "\n",
351
+ "Code that is written for a specific backend ('asyncio' or 'trio') can be run in the same thread with one of the following:\n",
352
+ "\n",
353
+ "- Line magic - The code following the magic on the same line is run using the specified backend.\n",
354
+ " - `%trio` \n",
355
+ " - `%asyncio` \n",
356
+ "\n",
357
+ "- Cell magic - The code block is run using the specified backend. \n",
358
+ " - `%%trio`\n",
359
+ " - `%asyncio`\n",
360
+ "\n",
361
+ "**Note: trio must be installed for this demo to work.**"
362
+ ]
363
+ },
364
+ {
365
+ "cell_type": "code",
366
+ "execution_count": null,
367
+ "id": "20",
368
+ "metadata": {},
369
+ "outputs": [],
370
+ "source": [
371
+ "%asyncio await asyncio.sleep(0) # This code gets run in an asyncio task \n",
372
+ "%trio await trio.sleep(0) # trio run as line magic"
373
+ ]
374
+ },
375
+ {
376
+ "cell_type": "code",
377
+ "execution_count": null,
378
+ "id": "21",
379
+ "metadata": {},
380
+ "outputs": [],
381
+ "source": [
382
+ "%%trio # trio cell magic\n",
383
+ "\n",
384
+ "def print_info():\n",
385
+ " from aiologic.lowlevel import current_async_library\n",
386
+ " print(f\"\"\"\n",
387
+ " Kernel backend: {get_ipython().kernel.interface.backend}\n",
388
+ " Current backend: { current_async_library()}\n",
389
+ " \"\"\") \n",
390
+ "\n",
391
+ "\n",
392
+ "print_info()\n",
393
+ "await trio.sleep(0)\n",
394
+ "\n",
395
+ "%callers\n",
396
+ "\n",
397
+ "%asyncio print_info()\n",
398
+ "%asyncio %callers\n",
399
+ "\n",
400
+ "await trio.sleep(0)"
401
+ ]
252
402
  }
253
403
  ],
254
404
  "metadata": {
@@ -19,7 +19,7 @@ from IPython.core.displaypub import DisplayPublisher
19
19
  from IPython.core.history import HistoryManager
20
20
  from IPython.core.interactiveshell import InteractiveShell
21
21
  from IPython.core.interactiveshell import _modified_open as _modified_open_ # pyright: ignore[reportPrivateUsage]
22
- from IPython.core.magic import Magics, line_cell_magic, line_magic, magics_class
22
+ from IPython.core.magic import Magics, line_cell_magic, line_magic, magics_class, no_var_expand
23
23
  from IPython.utils.tokenutil import token_at_cursor
24
24
  from jupyter_core.paths import jupyter_runtime_dir
25
25
  from traitlets import traitlets
@@ -32,7 +32,7 @@ from async_kernel.common import Fixed, KernelInterrupt, import_item
32
32
  from async_kernel.compiler import XCachingCompiler
33
33
  from async_kernel.event_loop.run import get_runtime_matplotlib_guis
34
34
  from async_kernel.pending import PendingManager
35
- from async_kernel.typing import Channel, Content, Message, NoValue, Tags
35
+ from async_kernel.typing import Channel, Content, Message, NoValue, RunMode, Tags
36
36
 
37
37
  if TYPE_CHECKING:
38
38
  from collections.abc import Callable, Generator
@@ -402,6 +402,14 @@ class AsyncInteractiveShell(InteractiveShell):
402
402
  except TypeError:
403
403
  return result
404
404
 
405
+ def transform_cell_async(self, raw_cell: str) -> str:
406
+ "Transform the cell and substitute magic calls with an awaitable wrapper."
407
+ return (
408
+ self.transform_cell(raw_cell)
409
+ .replace("get_ipython().run_line_magic(", "await get_ipython().run_line_magic_async(")
410
+ .replace("get_ipython().run_cell_magic(", "await get_ipython().run_cell_magic_async(")
411
+ )
412
+
405
413
  async def execute_request(
406
414
  self,
407
415
  code: str = "",
@@ -424,17 +432,11 @@ class AsyncInteractiveShell(InteractiveShell):
424
432
  try:
425
433
  tags: list[str] = utils.get_tags()
426
434
  timeout: float = utils.get_timeout(tags=tags)
427
- suppress_error: bool = Tags.suppress_error in tags
428
435
  raises_exception: bool = Tags.raises_exception in tags
429
436
  stop_on_error_override: bool = Tags.stop_on_error in tags
430
- transformed_cell = (
431
- self.transform_cell(code)
432
- .replace("get_ipython().run_line_magic(", "await get_ipython().run_line_magic_async(")
433
- .replace("get_ipython().run_cell_magic(", "await get_ipython().run_cell_magic_async(")
434
- )
435
437
  if stop_on_error_override:
436
438
  stop_on_error = utils.get_tag_value(Tags.stop_on_error, stop_on_error)
437
- elif suppress_error or raises_exception:
439
+ elif raises_exception:
438
440
  stop_on_error = False
439
441
 
440
442
  if silent:
@@ -464,7 +466,7 @@ class AsyncInteractiveShell(InteractiveShell):
464
466
  raw_cell=code,
465
467
  store_history=store_history,
466
468
  silent=silent,
467
- transformed_cell=transformed_cell,
469
+ transformed_cell=self.transform_cell_async(code),
468
470
  shell_futures=True,
469
471
  cell_id=cell_id,
470
472
  )
@@ -482,7 +484,7 @@ class AsyncInteractiveShell(InteractiveShell):
482
484
  self.events.trigger("post_execute")
483
485
  if not silent:
484
486
  self.events.trigger("post_run_cell", result)
485
- if (err) and (suppress_error or (isinstance(err, anyio.get_cancelled_exc_class()) and (timeout != 0))):
487
+ if (err) and (isinstance(err, anyio.get_cancelled_exc_class()) and (timeout != 0)):
486
488
  # Suppress the error due to either:
487
489
  # 1. tag
488
490
  # 2. timeout
@@ -583,10 +585,6 @@ class AsyncInteractiveShell(InteractiveShell):
583
585
 
584
586
  @override
585
587
  def _showtraceback(self, etype, evalue, stb) -> None:
586
- if Tags.suppress_error in utils.get_tags():
587
- if msg := utils.get_tag_value(Tags.suppress_error, "⚠"):
588
- print(msg)
589
- return
590
588
  if utils.get_timeout() != 0.0 and etype is anyio.get_cancelled_exc_class():
591
589
  etype, evalue, stb = TimeoutError, "Cell execute timeout", []
592
590
  self.kernel.interface.iopub_send(
@@ -931,6 +929,7 @@ class KernelMagics(Magics):
931
929
  )
932
930
  print(f"Current shell:\t{self.shell}\n\n{subshell_list}")
933
931
 
932
+ @no_var_expand
934
933
  @line_magic
935
934
  async def pip(self, line: str) -> Any | None:
936
935
  """Run the pip package manager for the current environment.
@@ -965,6 +964,7 @@ class KernelMagics(Magics):
965
964
 
966
965
  return None
967
966
 
967
+ @no_var_expand
968
968
  @line_magic
969
969
  async def uv(self, line) -> None:
970
970
  """Run the uv package manager for the current environment.
@@ -979,19 +979,55 @@ class KernelMagics(Magics):
979
979
  if process.stderr:
980
980
  tg.start_soon(_forward_transport_stream, process.stderr, sys.stdout)
981
981
 
982
+ @no_var_expand
982
983
  @line_cell_magic
983
- async def python(self, line: str, cell: str) -> None:
984
+ async def thread(self, line: str, cell: str | None = None) -> None:
984
985
  """
985
- Run python code.
986
+ Run the python code in a caller managed child thread.
986
987
 
987
- Useful only when the primary language is not Python.
988
+ Both line and cell magic are supported.
989
+
990
+ For cell_magic, [CallerCreateOptions][async_kernel.typing.CallerCreateOptions] can be passed as literals.
991
+
992
+ Example:
993
+ %%thread name="Trio executor" backend=trio
988
994
  """
989
- shell = SubshellManager.get_shell()
990
- cell = cell or line
991
- await shell.run_cell_async(
995
+ shell: AsyncInteractiveShell | AsyncInteractiveSubshell = SubshellManager.get_shell()
996
+ if cell is None:
997
+ cell = line
998
+ options: Any = None
999
+ else:
1000
+ options = RunMode.line_to_options(line)
1001
+ caller = shell.kernel.caller
1002
+ await (caller.get(**options).call_soon if options else caller.to_thread)(
1003
+ shell.run_cell_async,
992
1004
  raw_cell=cell,
993
1005
  store_history=False,
994
1006
  silent=True,
995
- cell_id=utils.get_cell_id(),
996
- transformed_cell=shell.transform_cell(cell),
1007
+ cell_id=None,
1008
+ transformed_cell=shell.transform_cell_async(cell),
997
1009
  )
1010
+
1011
+ async def _call_using_backend(self, backend: Literal["asyncio", "trio"], code: str):
1012
+ shell: AsyncInteractiveShell | AsyncInteractiveSubshell = SubshellManager.get_shell()
1013
+ await Caller().call_using_backend(
1014
+ backend,
1015
+ shell.run_cell_async,
1016
+ raw_cell=code,
1017
+ store_history=False,
1018
+ silent=True,
1019
+ cell_id=None,
1020
+ transformed_cell=shell.transform_cell_async(code),
1021
+ )
1022
+
1023
+ @no_var_expand
1024
+ @line_cell_magic
1025
+ async def asyncio(self, line: str, cell: str | None = None) -> None:
1026
+ ""
1027
+ await self._call_using_backend("asyncio", cell or line)
1028
+
1029
+ @no_var_expand
1030
+ @line_cell_magic
1031
+ async def trio(self, line: str, cell: str | None = None) -> None:
1032
+ ""
1033
+ await self._call_using_backend("trio", cell or line)
@@ -834,8 +834,7 @@ class Caller(anyio.AsyncContextManagerMixin):
834
834
  **kwargs: Keyword arguments to use with `func`.
835
835
 
836
836
  Notes:
837
-
838
- - **Only use this to execute coroutines that require a specific backend to run in the caller's thread.**
837
+ **Only use this method to execute coroutines that require a specific backend to run in the caller's thread.**
839
838
  """
840
839
  return self.schedule_call(func, args, kwargs, None, PendingTracker, Backend(backend))
841
840