async-kernel 0.17.1__tar.gz → 0.18.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.1 → async_kernel-0.18.1}/.vscode/launch.json +1 -1
  2. {async_kernel-0.17.1 → async_kernel-0.18.1}/CHANGELOG.md +48 -0
  3. {async_kernel-0.17.1 → async_kernel-0.18.1}/PKG-INFO +1 -1
  4. {async_kernel-0.17.1 → async_kernel-0.18.1}/_version.py +2 -2
  5. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/notebooks/custom_kernel.ipynb +9 -7
  6. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/__init__.py +2 -1
  7. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/asyncshell.py +52 -97
  8. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/caller.py +65 -56
  9. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/interface/base.py +13 -7
  10. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/interface/zmq.py +1 -1
  11. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/iostream.py +17 -18
  12. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/kernel.py +72 -12
  13. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/kernelspec.py +1 -1
  14. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/utils.py +40 -0
  15. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_iostream.py +2 -1
  16. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_kernel.py +20 -1
  17. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/dependabot.yaml +0 -0
  18. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/release.yml +0 -0
  19. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/ci.yml +0 -0
  20. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/enforce-label.yml +0 -0
  21. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/new_release.yml +0 -0
  22. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/pre-commit.yml +0 -0
  23. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/publish-docs.yml +0 -0
  24. {async_kernel-0.17.1 → async_kernel-0.18.1}/.github/workflows/publish-to-pypi.yml +0 -0
  25. {async_kernel-0.17.1 → async_kernel-0.18.1}/.gitignore +0 -0
  26. {async_kernel-0.17.1 → async_kernel-0.18.1}/.pre-commit-config.yaml +0 -0
  27. {async_kernel-0.17.1 → async_kernel-0.18.1}/.vscode/settings.json +0 -0
  28. {async_kernel-0.17.1 → async_kernel-0.18.1}/.vscode/spellright.dict +0 -0
  29. {async_kernel-0.17.1 → async_kernel-0.18.1}/CONTRIBUTING.md +0 -0
  30. {async_kernel-0.17.1 → async_kernel-0.18.1}/IPYTHON_LICENSE +0 -0
  31. {async_kernel-0.17.1 → async_kernel-0.18.1}/LICENSE +0 -0
  32. {async_kernel-0.17.1 → async_kernel-0.18.1}/README.md +0 -0
  33. {async_kernel-0.17.1 → async_kernel-0.18.1}/cliff.toml +0 -0
  34. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/about/changelog.md +0 -0
  35. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/about/contributing.md +0 -0
  36. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/about/index.md +0 -0
  37. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/about/license.md +0 -0
  38. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/index.md +0 -0
  39. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/javascripts/extra.js +0 -0
  40. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/notebooks/caller.ipynb +0 -0
  41. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/notebooks/concurrency.ipynb +0 -0
  42. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/overrides/main.html +0 -0
  43. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/asyncshell.md +0 -0
  44. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/caller.md +0 -0
  45. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/comm.md +0 -0
  46. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/command.md +0 -0
  47. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/common.md +0 -0
  48. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/debugger.md +0 -0
  49. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/event_loop.md +0 -0
  50. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/index.md +0 -0
  51. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/interface.md +0 -0
  52. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/kernel.md +0 -0
  53. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/kernelspec.md +0 -0
  54. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/pending.md +0 -0
  55. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/typing.md +0 -0
  56. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/reference/utils.md +0 -0
  57. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/stylesheets/extra.css +0 -0
  58. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/thread_safety.md +0 -0
  59. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/usage/commands.md +0 -0
  60. {async_kernel-0.17.1 → async_kernel-0.18.1}/docs/usage/index.md +0 -0
  61. {async_kernel-0.17.1 → async_kernel-0.18.1}/hatch_build.py +0 -0
  62. {async_kernel-0.17.1 → async_kernel-0.18.1}/mkdocs.yml +0 -0
  63. {async_kernel-0.17.1 → async_kernel-0.18.1}/pyproject.toml +0 -0
  64. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/__main__.py +0 -0
  65. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/comm.py +0 -0
  66. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/command.py +0 -0
  67. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/common.py +0 -0
  68. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/compat/json.py +0 -0
  69. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/compiler.py +0 -0
  70. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/debugger.py +0 -0
  71. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/event_loop/__init__.py +0 -0
  72. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
  73. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/event_loop/qt_host.py +0 -0
  74. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/event_loop/run.py +0 -0
  75. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/event_loop/tk_host.py +0 -0
  76. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/interface/__init__.py +0 -0
  77. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/interface/callable.py +0 -0
  78. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/pending.py +0 -0
  79. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/py.typed +0 -0
  80. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/resources/logo-32x32.png +0 -0
  81. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/resources/logo-64x64.png +0 -0
  82. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/resources/logo-svg.svg +0 -0
  83. {async_kernel-0.17.1 → async_kernel-0.18.1}/src/async_kernel/typing.py +0 -0
  84. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/__init__.py +0 -0
  85. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/conftest.py +0 -0
  86. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/references.py +0 -0
  87. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_callable_kernel_interface.py +0 -0
  88. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_caller.py +0 -0
  89. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_comm.py +0 -0
  90. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_command.py +0 -0
  91. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_common.py +0 -0
  92. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_compat.py +0 -0
  93. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_debugger.py +0 -0
  94. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_enter_kernel.py +0 -0
  95. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_event_loop.py +0 -0
  96. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_kernel_subclass.py +0 -0
  97. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_kernelspec.py +0 -0
  98. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_message_spec.py +0 -0
  99. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_pending.py +0 -0
  100. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_typing.py +0 -0
  101. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_utils.py +0 -0
  102. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/test_zmq_messaging.py +0 -0
  103. {async_kernel-0.17.1 → async_kernel-0.18.1}/tests/utils.py +0 -0
  104. {async_kernel-0.17.1 → async_kernel-0.18.1}/uv.lock +0 -0
@@ -34,7 +34,7 @@
34
34
  "args": [
35
35
  "--notebook-dir=docs/notebooks",
36
36
  "--no-browser",
37
- "--ServerApp.token=''",
37
+ "--IdentityProvider.token=''",
38
38
  "--port=9991"
39
39
  ],
40
40
  "cwd": "${workspaceFolder}",
@@ -5,6 +5,50 @@ 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.18.1] - 2026-04-26
9
+
10
+ ### <!-- 2 --> 🐛 Fixes
11
+
12
+ - Refactor OutStream and fix utils.redirect_stderr. [#450](https://github.com/fleming79/async-kernel/pull/450)
13
+
14
+ ### <!-- 6 --> 🌀 Miscellaneous
15
+
16
+ - Check trio is available early when creating a Caller for a trio backend. [#452](https://github.com/fleming79/async-kernel/pull/452)
17
+
18
+ - Remove unnecessary line. [#451](https://github.com/fleming79/async-kernel/pull/451)
19
+
20
+ ## [0.18.0] - 2026-04-25
21
+
22
+ ### <!-- 0 --> 🏗️ Breaking changes
23
+
24
+ - Move kernel_info, help_links and debugger from the shell to the kernel or interface. [#444](https://github.com/fleming79/async-kernel/pull/444)
25
+
26
+ ### <!-- 1 --> 🚀 Features
27
+
28
+ - Add utils.redirect_stdout and utils.redirect_stderr. [#442](https://github.com/fleming79/async-kernel/pull/442)
29
+
30
+ - Make system calls asynchronous and convert uv and pip magics to use it. [#441](https://github.com/fleming79/async-kernel/pull/441)
31
+
32
+ ### <!-- 2 --> 🐛 Fixes
33
+
34
+ - Improve uv support and fix example kernel spec [#440](https://github.com/fleming79/async-kernel/pull/440)
35
+
36
+ ### <!-- 5 --> 📝 Documentation
37
+
38
+ - Update Caller docstrings [#446](https://github.com/fleming79/async-kernel/pull/446)
39
+
40
+ ### <!-- 6 --> 🌀 Miscellaneous
41
+
42
+ - Prepare for release v0.18.0 [#449](https://github.com/fleming79/async-kernel/pull/449)
43
+
44
+ - Refactor AsyncInteractiveShell.shell method [#448](https://github.com/fleming79/async-kernel/pull/448)
45
+
46
+ - Refactor Caller.as_completed. [#447](https://github.com/fleming79/async-kernel/pull/447)
47
+
48
+ - Change kernel_info back to a property. [#445](https://github.com/fleming79/async-kernel/pull/445)
49
+
50
+ - Change magics thread, asynio and trio back to coroutines that don't return results. [#443](https://github.com/fleming79/async-kernel/pull/443)
51
+
8
52
  ## [0.17.1] - 2026-04-23
9
53
 
10
54
  ### <!-- 0 --> 🏗️ Breaking changes
@@ -21,6 +65,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
65
 
22
66
  ### <!-- 6 --> 🌀 Miscellaneous
23
67
 
68
+ - Prepare for release v0.17.1 [#439](https://github.com/fleming79/async-kernel/pull/439)
69
+
24
70
  - Update pre-commit, uv.lock and basedpyright [#438](https://github.com/fleming79/async-kernel/pull/438)
25
71
 
26
72
  ## [0.17.0] - 2026-04-22
@@ -1219,6 +1265,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1219
1265
 
1220
1266
  - Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
1221
1267
 
1268
+ [0.18.1]: https://github.com/fleming79/async-kernel/compare/v0.18.0..v0.18.1
1269
+ [0.18.0]: https://github.com/fleming79/async-kernel/compare/v0.17.1..v0.18.0
1222
1270
  [0.17.1]: https://github.com/fleming79/async-kernel/compare/v0.17.0..v0.17.1
1223
1271
  [0.17.0]: https://github.com/fleming79/async-kernel/compare/v0.16.4..v0.17.0
1224
1272
  [0.16.4]: https://github.com/fleming79/async-kernel/compare/v0.16.3..v0.16.4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async-kernel
3
- Version: 0.17.1
3
+ Version: 0.18.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.1'
22
- __version_tuple__ = version_tuple = (0, 17, 1)
21
+ __version__ = version = '0.18.1'
22
+ __version_tuple__ = version_tuple = (0, 18, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -19,7 +19,9 @@
19
19
  "\n",
20
20
  "We can use uv to create a virtual environment for different versions of Python.\n",
21
21
  "\n",
22
- "Let's create a new virtual environment in the folder named \".venv_py315\" using uv."
22
+ "Let's create a new virtual environment in the folder named \".venv_py315\" using uv.\n",
23
+ "\n",
24
+ "**Requires a recent version of uv to be installed.**"
23
25
  ]
24
26
  },
25
27
  {
@@ -29,7 +31,7 @@
29
31
  "metadata": {},
30
32
  "outputs": [],
31
33
  "source": [
32
- "%uv venv .venv_py315 --python 3.15\n",
34
+ "%uv venv .venv_py315 --python 3.15 --clear\n",
33
35
  "%uv pip install --directory .venv_py315 --python 3.15 async-kernel"
34
36
  ]
35
37
  },
@@ -56,10 +58,10 @@
56
58
  "assert uv_path.exists()\n",
57
59
  "\n",
58
60
  "write_kernel_spec(\n",
59
- " kernel_name=\"async-kernel-315\",\n",
60
- " display_name=\"Python 3.15 (async kernel)\",\n",
61
- " env={\"UV_PROJECT_ENVIRONMENT\": uv_path.as_posix()},\n",
62
- " executable=(\"uv\", \"run\", \"python\", \"-m\"),\n",
61
+ " kernel_name=\"async_3.15\",\n",
62
+ " display_name=\"Python 3.15 (async)\",\n",
63
+ " env={\"UV_PROJECT_ENVIRONMENT\": str(uv_path)},\n",
64
+ " executable=(\"uv\", \"run\", \"--no-sync\", \"python\", \"-m\", \"async_kernel\"),\n",
63
65
  ")"
64
66
  ]
65
67
  },
@@ -68,7 +70,7 @@
68
70
  "id": "5",
69
71
  "metadata": {},
70
72
  "source": [
71
- "The kernel spec with the display name \"Python 3.15 (async kernel)\" has been added."
73
+ "The kernel spec with the display name \"Python 3.15 (async kernel)\" has been added. You will need to refresh the list of kernels for it to be available."
72
74
  ]
73
75
  }
74
76
  ],
@@ -7,7 +7,8 @@ from async_kernel.kernel import Kernel
7
7
  from async_kernel.kernelspec import PROTOCOL_VERSION
8
8
  from async_kernel.pending import Pending
9
9
 
10
- __version__ = version(distribution_name="async-kernel")
10
+ distribution_name = "async-kernel"
11
+ __version__ = version(distribution_name=distribution_name)
11
12
 
12
13
  kernel_protocol_version = PROTOCOL_VERSION
13
14
  kernel_protocol_version_info = {
@@ -5,6 +5,7 @@ import contextlib
5
5
  import json
6
6
  import os
7
7
  import pathlib
8
+ import shlex
8
9
  import sys
9
10
  import time
10
11
  from collections.abc import Callable
@@ -13,6 +14,7 @@ from typing import TYPE_CHECKING, Any, Literal, Never, Self, TextIO, final
13
14
  import anyio
14
15
  import IPython.core.release
15
16
  from aiologic.lowlevel import async_checkpoint
17
+ from anyio.streams.text import TextReceiveStream
16
18
  from IPython.core.completer import provisionalcompleter, rectify_completions
17
19
  from IPython.core.displayhook import DisplayHook
18
20
  from IPython.core.displaypub import DisplayPublisher
@@ -28,10 +30,10 @@ from typing_extensions import override
28
30
  import async_kernel
29
31
  from async_kernel import utils
30
32
  from async_kernel.caller import Caller
31
- from async_kernel.common import Fixed, KernelInterrupt, import_item
33
+ from async_kernel.common import Fixed, KernelInterrupt
32
34
  from async_kernel.compiler import XCachingCompiler
33
35
  from async_kernel.event_loop.run import get_runtime_matplotlib_guis
34
- from async_kernel.pending import PendingManager
36
+ from async_kernel.pending import Pending, PendingManager
35
37
  from async_kernel.typing import Channel, Content, Message, NoValue, RunMode, Tags
36
38
 
37
39
  if TYPE_CHECKING:
@@ -40,19 +42,11 @@ if TYPE_CHECKING:
40
42
  from anyio.abc import ByteReceiveStream
41
43
 
42
44
  from async_kernel import Kernel
43
- from async_kernel.debugger import Debugger
44
45
 
45
46
 
46
47
  __all__ = ["AsyncInteractiveShell"]
47
48
 
48
49
 
49
- async def _forward_transport_stream(transport_stream: ByteReceiveStream, out: TextIO, /) -> None:
50
- from anyio.streams.text import TextReceiveStream # noqa: PLC0415
51
-
52
- async for text in TextReceiveStream(transport_stream):
53
- out.write(text)
54
-
55
-
56
50
  class AsyncDisplayHook(DisplayHook):
57
51
  """
58
52
  A displayhook subclass that publishes data using [iopub_send][async_kernel.interface.base.BaseKernelInterface.iopub_send].
@@ -207,9 +201,6 @@ class AsyncInteractiveShell(InteractiveShell):
207
201
  pending_manager = Fixed(ShellPendingManager)
208
202
  subshell_id = Fixed(lambda _: None)
209
203
 
210
- debugger: Fixed[Self, Debugger | None] | None = None # pyright: ignore[reportIncompatibleMethodOverride]
211
- "Handles [debug requests](https://jupyter-client.readthedocs.io/en/stable/messaging.html#debug-request)."
212
-
213
204
  user_ns_hidden: Fixed[Self, dict] = Fixed(lambda c: c["owner"]._get_default_ns())
214
205
  user_global_ns: Fixed[Self, dict] = Fixed(lambda c: c["owner"]._user_ns) # pyright: ignore[reportIncompatibleMethodOverride]
215
206
 
@@ -228,9 +219,6 @@ class AsyncInteractiveShell(InteractiveShell):
228
219
  loop_runner = None
229
220
  autoindent = False
230
221
 
231
- help_links = traitlets.Tuple()
232
- ""
233
-
234
222
  @override
235
223
  def __repr__(self) -> str:
236
224
  return f"<{self.__class__.__name__} kernel_name: {self.kernel.kernel_name!r} subshell_id: {self.subshell_id}>"
@@ -363,51 +351,59 @@ class AsyncInteractiveShell(InteractiveShell):
363
351
  def ns_table(self) -> dict[str, dict[Any, Any] | dict[str, Any]]:
364
352
  return {"user_global": self.user_global_ns, "user_local": self.user_ns, "builtin": builtins.__dict__}
365
353
 
366
- @property
367
- def kernel_info(self) -> dict[str, Any]:
368
- "A dict of detail sent in reply to for a 'kernel_info_request'."
369
- return {
370
- "protocol_version": async_kernel.kernel_protocol_version,
371
- "implementation": "async_kernel",
372
- "implementation_version": async_kernel.__version__,
373
- "language_info": async_kernel.kernel_protocol_version_info,
374
- "banner": self.banner,
375
- "help_links": self.help_links,
376
- "debugger": bool(self.debugger),
377
- "kernel_name": self.kernel.kernel_name,
378
- "supported_features": self.supported_features,
379
- }
380
-
381
- @property
382
- def supported_features(self) -> list[str]:
383
- "Supported features included in the reply to a [async_kernel.kernel.Kernel.kernel_info_request][]."
384
- features = ["kernel subshells"]
385
- if self.debugger:
386
- features.append("debugger")
387
- return features
388
-
389
354
  async def run_line_magic_async(self, magic_name: str, line: str, _stack_depth=1) -> Any:
390
355
  "Call and awaits [run_line_magic][IPython.core.interactiveshell.InteractiveShell.run_line_magic]."
391
- result = self.run_line_magic(magic_name, line, _stack_depth)
392
- try:
393
- return await result # pyright: ignore[reportGeneralTypeIssues]
394
- except TypeError:
395
- return result
356
+ async with Caller().create_pending_group(mode=1):
357
+ result = self.run_line_magic(magic_name, line, _stack_depth)
358
+ try:
359
+ return await result # pyright: ignore[reportGeneralTypeIssues]
360
+ except TypeError:
361
+ return result
396
362
 
397
363
  async def run_cell_magic_async(self, magic_name: str, line: str, cell: str) -> Any:
398
364
  "Call and awaits [run_cell_magic][IPython.core.interactiveshell.InteractiveShell.run_cell_magic]."
399
- result = self.run_cell_magic(magic_name, line, cell)
400
- try:
401
- return await result # pyright: ignore[reportGeneralTypeIssues]
402
- except TypeError:
403
- return result
365
+ async with Caller().create_pending_group(mode=1):
366
+ result = self.run_cell_magic(magic_name, line, cell)
367
+ try:
368
+ return await result # pyright: ignore[reportGeneralTypeIssues]
369
+ except TypeError:
370
+ return result
371
+
372
+ @override
373
+ def system(self, cmd: list[str] | str, *, stderr_to_stdout: bool = False, **kwargs: Any) -> Pending[None]:
374
+ """
375
+ Make a system call in a separate thread.
376
+
377
+ Args:
378
+ cmd: Passed as the first argument 'command' when calling [anyio.open_process][].
379
+ stderr_to_stdout: Send stderr output to stdout.
380
+ **kwargs: Keyword arguments are passed to [anyio.open_process][].
381
+
382
+ Tip:
383
+ - The output can be redicted by making the call in the context of
384
+ [async_kernel.utils.redirect_stdout][] and/or [async_kernel.utils.redirect_stderr][].
385
+ """
386
+
387
+ async def forward_output(transport_stream: ByteReceiveStream | None, out: TextIO, /) -> None:
388
+ if transport_stream:
389
+ async for text in TextReceiveStream(transport_stream):
390
+ out.write(text)
391
+
392
+ async def open_process() -> None:
393
+ async with await anyio.open_process(cmd, **kwargs) as process, anyio.create_task_group() as tg:
394
+ tg.start_soon(forward_output, process.stdout, sys.stdout)
395
+ tg.start_soon(forward_output, process.stderr, sys.stdout if stderr_to_stdout else sys.stderr)
396
+
397
+ return Caller().to_thread(open_process)
404
398
 
405
399
  def transform_cell_async(self, raw_cell: str) -> str:
406
400
  "Transform the cell and substitute magic calls with an awaitable wrapper."
401
+
407
402
  return (
408
403
  self.transform_cell(raw_cell)
409
404
  .replace("get_ipython().run_line_magic(", "await get_ipython().run_line_magic_async(")
410
405
  .replace("get_ipython().run_cell_magic(", "await get_ipython().run_cell_magic_async(")
406
+ .replace("get_ipython().system(", "await get_ipython().system(")
411
407
  )
412
408
 
413
409
  async def execute_request(
@@ -611,6 +607,9 @@ class AsyncInteractiveShell(InteractiveShell):
611
607
  """Initialize magics."""
612
608
  super().init_magics()
613
609
  self.register_magics(KernelMagics)
610
+ self.magics_manager.register_alias("python", "thread")
611
+ self.magics_manager.register_alias("python3", "thread")
612
+ self.magics_manager.register_alias("!", "system")
614
613
 
615
614
  @override
616
615
  def enable_gui(self, gui=None) -> None:
@@ -760,39 +759,6 @@ class AsyncInteractiveSubshell(AsyncInteractiveShell):
760
759
  class IPythonAsyncInteractiveShell(AsyncInteractiveShell):
761
760
  "The default AsyncInteractiveShell"
762
761
 
763
- debugger = Fixed(
764
- lambda _: (
765
- import_item("async_kernel.debugger.Debugger")()
766
- if (not utils.LAUNCHED_BY_DEBUGPY) & (sys.platform != "emscripten")
767
- else None
768
- )
769
- )
770
-
771
- @traitlets.default("help_links")
772
- def _default_help_links(self) -> tuple[dict[str, str], ...]:
773
- return (
774
- {
775
- "text": "Async Kernel Reference ",
776
- "url": "https://fleming79.github.io/async-kernel/",
777
- },
778
- {
779
- "text": "IPython Reference",
780
- "url": "https://ipython.readthedocs.io/en/stable/",
781
- },
782
- {
783
- "text": "IPython magic Reference",
784
- "url": "https://ipython.readthedocs.io/en/stable/interactive/magics.html",
785
- },
786
- {
787
- "text": "Matplotlib ipympl Reference",
788
- "url": "https://matplotlib.org/ipympl/",
789
- },
790
- {
791
- "text": "Matplotlib Reference",
792
- "url": "https://matplotlib.org/contents.html",
793
- },
794
- )
795
-
796
762
 
797
763
  class IPythonInteractiveSubshell(AsyncInteractiveSubshell):
798
764
  "The default AsyncInteractiveSubshell"
@@ -955,29 +921,18 @@ class KernelMagics(Magics):
955
921
  case _ as name:
956
922
  print("Unsupported command:", name)
957
923
  else:
958
- cmd = [sys.executable, "-m", "pip", *line.split()]
959
- async with await anyio.open_process(cmd) as process, anyio.create_task_group() as tg:
960
- if process.stdout:
961
- tg.start_soon(_forward_transport_stream, process.stdout, sys.stdout)
962
- if process.stderr:
963
- tg.start_soon(_forward_transport_stream, process.stderr, sys.stderr)
964
-
924
+ await SubshellManager.get_shell().system([sys.executable, "-m", "pip", *line.split()])
965
925
  return None
966
926
 
967
927
  @no_var_expand
968
928
  @line_magic
969
- async def uv(self, line) -> None:
929
+ def uv(self, line) -> Pending[None]:
970
930
  """Run the uv package manager for the current environment.
971
931
 
972
932
  Usage:
973
933
  %uv pip install [pkgs]
974
934
  """
975
- cmd = ["uv", *line.split()]
976
- async with await anyio.open_process(cmd) as process, anyio.create_task_group() as tg:
977
- if process.stdout:
978
- tg.start_soon(_forward_transport_stream, process.stdout, sys.stdout)
979
- if process.stderr:
980
- tg.start_soon(_forward_transport_stream, process.stderr, sys.stdout)
935
+ return SubshellManager.get_shell().system(["uv", *shlex.split(line)], stderr_to_stdout=True)
981
936
 
982
937
  @no_var_expand
983
938
  @line_cell_magic
@@ -1008,7 +963,7 @@ class KernelMagics(Magics):
1008
963
  transformed_cell=shell.transform_cell_async(cell),
1009
964
  )
1010
965
 
1011
- async def _call_using_backend(self, backend: Literal["asyncio", "trio"], code: str):
966
+ async def _call_using_backend(self, backend: Literal["asyncio", "trio"], code: str) -> None:
1012
967
  shell: AsyncInteractiveShell | AsyncInteractiveSubshell = SubshellManager.get_shell()
1013
968
  await Caller().call_using_backend(
1014
969
  backend,