async-kernel 0.19.1__tar.gz → 0.19.2__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.
- {async_kernel-0.19.1 → async_kernel-0.19.2}/CHANGELOG.md +14 -1
- {async_kernel-0.19.1 → async_kernel-0.19.2}/PKG-INFO +1 -1
- {async_kernel-0.19.1 → async_kernel-0.19.2}/_version.py +2 -2
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/kernel.py +36 -41
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/shell/base.py +27 -12
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/shell/ipshell.py +119 -105
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/utils.py +3 -13
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_callable_interface.py +22 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_kernel_ipshell.py +3 -8
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_message_spec.py +1 -1
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_zmq_interface.py +4 -4
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/dependabot.yaml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/release.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/ci.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/enforce-label.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/new_release.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/pre-commit.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/publish-docs.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.gitignore +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.pre-commit-config.yaml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.vscode/launch.json +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.vscode/settings.json +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/.vscode/spellright.dict +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/CONTRIBUTING.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/IPYTHON_LICENSE +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/LICENSE +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/README.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/cliff.toml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/about/changelog.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/about/contributing.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/about/index.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/about/license.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/index.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/javascripts/extra.js +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/notebooks/caller.ipynb +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/notebooks/concurrency.ipynb +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/notebooks/custom_kernel.ipynb +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/overrides/main.html +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/caller.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/comm.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/command.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/common.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/debugger.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/event_loop.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/index.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/interface.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/ipshell.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/kernel.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/kernelspec.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/pending.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/shell.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/typing.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/reference/utils.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/stylesheets/extra.css +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/thread_safety.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/usage/commands.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/docs/usage/index.md +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/hatch_build.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/mkdocs.yml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/pyproject.toml +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/__init__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/__main__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/caller.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/comm.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/command.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/common.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/compat/attr_docs.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/compat/json.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/compiler.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/debugger.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/event_loop/__init__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/event_loop/asyncio_guest.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/event_loop/qt_host.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/event_loop/run.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/event_loop/tk_host.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/interface/__init__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/interface/base.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/interface/callable.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/interface/zmq.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/kernelspec.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/pending.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/py.typed +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/resources/logo-32x32.png +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/resources/logo-64x64.png +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/resources/logo-svg.svg +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/shell/__init__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/src/async_kernel/typing.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/__init__.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/conftest.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/references.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_base_interface.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_caller.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_comm.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_command.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_common.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_compat.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_debugger.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_debugger_static.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_enter_kernel.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_event_loop.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_iostream.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_kernelspec.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_pending.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_subclass.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_typing.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_utils.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/test_zmq_messaging.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/tests/utils.py +0 -0
- {async_kernel-0.19.1 → async_kernel-0.19.2}/uv.lock +0 -0
|
@@ -5,7 +5,17 @@ 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.19.
|
|
8
|
+
## [0.19.2] - 2026-06-04
|
|
9
|
+
|
|
10
|
+
### <!-- 0 --> 🏗️ Breaking changes
|
|
11
|
+
|
|
12
|
+
- Rename methods on the shell to more resemble those on the kerenel. [#477](https://github.com/fleming79/async-kernel/pull/477)
|
|
13
|
+
|
|
14
|
+
### <!-- 6 --> 🌀 Miscellaneous
|
|
15
|
+
|
|
16
|
+
- Run do_execute in tasks [#478](https://github.com/fleming79/async-kernel/pull/478)
|
|
17
|
+
|
|
18
|
+
## [0.19.1] - 2026-05-27
|
|
9
19
|
|
|
10
20
|
### <!-- 2 --> 🐛 Fixes
|
|
11
21
|
|
|
@@ -15,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
25
|
|
|
16
26
|
### <!-- 6 --> 🌀 Miscellaneous
|
|
17
27
|
|
|
28
|
+
- Prepare for release v0.19.1 [#476](https://github.com/fleming79/async-kernel/pull/476)
|
|
29
|
+
|
|
18
30
|
- Bump setup-uv from v7 to v8.1.0. [#473](https://github.com/fleming79/async-kernel/pull/473)
|
|
19
31
|
|
|
20
32
|
## [0.19.0] - 2026-05-26
|
|
@@ -1323,6 +1335,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1323
1335
|
|
|
1324
1336
|
- Bump the actions group across 1 directory with 2 updates [#3](https://github.com/fleming79/async-kernel/pull/3)
|
|
1325
1337
|
|
|
1338
|
+
[0.19.2]: https://github.com/fleming79/async-kernel/compare/v0.19.1..v0.19.2
|
|
1326
1339
|
[0.19.1]: https://github.com/fleming79/async-kernel/compare/v0.19.0..v0.19.1
|
|
1327
1340
|
[0.19.0]: https://github.com/fleming79/async-kernel/compare/v0.18.3..v0.19.0
|
|
1328
1341
|
[0.18.3]: https://github.com/fleming79/async-kernel/compare/v0.18.2..v0.18.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-kernel
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.2
|
|
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.19.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 19,
|
|
21
|
+
__version__ = version = '0.19.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 19, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -44,6 +44,7 @@ if TYPE_CHECKING:
|
|
|
44
44
|
from contextvars import ContextVar
|
|
45
45
|
from types import CoroutineType, FrameType
|
|
46
46
|
|
|
47
|
+
from async_kernel.pending import Pending
|
|
47
48
|
from async_kernel.typing import Content, Message
|
|
48
49
|
|
|
49
50
|
__all__ = ["Kernel", "KernelInterrupt"]
|
|
@@ -156,8 +157,10 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
156
157
|
comm_manager = Fixed(CommManager)
|
|
157
158
|
"Creates [async_kernel.comm.Comm][] instances and maintains a mapping to `comm_id` to `Comm` instances."
|
|
158
159
|
|
|
159
|
-
|
|
160
|
-
"A set
|
|
160
|
+
active_execute_requests: Fixed[Self, set[Pending[Any]]] = Fixed(set)
|
|
161
|
+
"A set of active execute requests that gets updated by the shell."
|
|
162
|
+
|
|
163
|
+
_interrupt_message = "Kernel interrupted"
|
|
161
164
|
|
|
162
165
|
_restart = False
|
|
163
166
|
_handler_cache: ClassVar[dict[tuple[str | None, MsgType, Callable], HandlerType]] = {}
|
|
@@ -319,17 +322,15 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
319
322
|
else:
|
|
320
323
|
os.kill(os.getpid(), signal.SIGINT)
|
|
321
324
|
|
|
322
|
-
def
|
|
325
|
+
def do_interrupt(self) -> None:
|
|
323
326
|
"""
|
|
324
|
-
|
|
327
|
+
Interrupt/cancel non-silent active execute requests.
|
|
325
328
|
"""
|
|
326
329
|
if (sys.platform != "emscripten") and (not self.debugger.enabled or not self.debugger.stopped_threads):
|
|
327
330
|
self._interrupt_now()
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
self.
|
|
331
|
-
except Exception:
|
|
332
|
-
pass
|
|
331
|
+
for pen in tuple(self.active_execute_requests):
|
|
332
|
+
if not pen.metadata.get("kwargs", {}).get("silent", False):
|
|
333
|
+
pen.cancel(self._interrupt_message)
|
|
333
334
|
|
|
334
335
|
def _patch_signal(self) -> Callable[[], None]:
|
|
335
336
|
|
|
@@ -541,22 +542,27 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
541
542
|
|
|
542
543
|
async def execute_request(self, job: Job[ExecuteContent], /) -> Content:
|
|
543
544
|
"""Handle an [execute request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute)."""
|
|
544
|
-
return await self.shell.
|
|
545
|
+
return await self.shell.do_execute(
|
|
546
|
+
cell_id=job["msg"]["metadata"].get("cellId"),
|
|
547
|
+
received_time=job["received_time"],
|
|
548
|
+
tags=job["msg"]["metadata"].get("tags", ()),
|
|
549
|
+
**job["msg"]["content"], # pyright: ignore[reportArgumentType]
|
|
550
|
+
)
|
|
545
551
|
|
|
546
552
|
async def complete_request(self, job: Job[Content], /) -> Content:
|
|
547
553
|
"""Handle an [completion request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#completion)."""
|
|
548
|
-
return await self.shell.
|
|
554
|
+
return await self.shell.do_complete(
|
|
549
555
|
code=job["msg"]["content"].get("code", ""), cursor_pos=job["msg"]["content"].get("cursor_pos", 0)
|
|
550
556
|
)
|
|
551
557
|
|
|
552
558
|
async def is_complete_request(self, job: Job[Content], /) -> Content:
|
|
553
559
|
"""Handle an [is_complete request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#code-completeness)."""
|
|
554
|
-
return await self.shell.
|
|
560
|
+
return await self.shell.is_complete(job["msg"]["content"].get("code", ""))
|
|
555
561
|
|
|
556
562
|
async def inspect_request(self, job: Job[Content], /) -> Content:
|
|
557
563
|
"""Handle an [inspect request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#introspection)."""
|
|
558
564
|
c = job["msg"]["content"]
|
|
559
|
-
return await self.shell.
|
|
565
|
+
return await self.shell.do_inspect(
|
|
560
566
|
code=c.get("code", ""),
|
|
561
567
|
cursor_pos=c.get("cursor_pos", 0),
|
|
562
568
|
detail_level=c.get("detail_level", 0),
|
|
@@ -564,7 +570,7 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
564
570
|
|
|
565
571
|
async def history_request(self, job: Job[Content], /) -> Content:
|
|
566
572
|
"""Handle an [history request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#history)."""
|
|
567
|
-
return await self.shell.
|
|
573
|
+
return await self.shell.do_history(**job["msg"]["content"])
|
|
568
574
|
|
|
569
575
|
async def comm_open(self, job: Job[Content], /) -> None:
|
|
570
576
|
"""Handle an [comm open request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#opening-a-comm)."""
|
|
@@ -580,7 +586,7 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
580
586
|
|
|
581
587
|
async def interrupt_request(self, job: Job[Content], /) -> Content:
|
|
582
588
|
"""Handle an [interrupt request](https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-interrupt)."""
|
|
583
|
-
self.
|
|
589
|
+
self.do_interrupt()
|
|
584
590
|
return {}
|
|
585
591
|
|
|
586
592
|
async def shutdown_request(self, job: Job[Content], /) -> Content:
|
|
@@ -624,11 +630,13 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
624
630
|
|
|
625
631
|
async def do_complete(self, code: str, cursor_pos: int | None) -> Content:
|
|
626
632
|
"Matches signature of [ipykernel.kernelbase.Kernel.do_complete][]."
|
|
627
|
-
return await self.shell.
|
|
633
|
+
return await self.shell.do_complete(code=code, cursor_pos=cursor_pos)
|
|
628
634
|
|
|
629
|
-
async def do_inspect(
|
|
635
|
+
async def do_inspect(
|
|
636
|
+
self, code: str, cursor_pos: int = 0, detail_level: Literal[0, 1] = 0, omit_sections=()
|
|
637
|
+
) -> Content:
|
|
630
638
|
"Matches signature of [ipykernel.kernelbase.Kernel.do_inspect][]."
|
|
631
|
-
return await self.shell.
|
|
639
|
+
return await self.shell.do_inspect(code=code, cursor_pos=cursor_pos, detail_level=detail_level)
|
|
632
640
|
|
|
633
641
|
async def do_history(
|
|
634
642
|
self,
|
|
@@ -643,7 +651,7 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
643
651
|
unique=False,
|
|
644
652
|
) -> Content:
|
|
645
653
|
"Matches signature of [ipykernel.kernelbase.Kernel.do_history][]."
|
|
646
|
-
return await self.shell.
|
|
654
|
+
return await self.shell.do_history(
|
|
647
655
|
output=output,
|
|
648
656
|
raw=raw,
|
|
649
657
|
hist_access_type=hist_access_type,
|
|
@@ -655,37 +663,24 @@ class Kernel(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_interf
|
|
|
655
663
|
async def do_execute(
|
|
656
664
|
self,
|
|
657
665
|
code: str,
|
|
658
|
-
silent: bool,
|
|
666
|
+
silent: bool = True,
|
|
659
667
|
store_history: bool = True,
|
|
660
668
|
user_expressions: dict[str, str] | None = None,
|
|
661
669
|
allow_stdin: bool = False,
|
|
662
670
|
*,
|
|
663
671
|
cell_meta: dict[str, Any] | None = None,
|
|
664
672
|
cell_id: str | None = None,
|
|
673
|
+
**_ignored,
|
|
665
674
|
) -> Content:
|
|
666
675
|
"Matches signature of [ipykernel.kernelbase.Kernel.do_execute][]."
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
code=code,
|
|
675
|
-
silent=silent,
|
|
676
|
-
store_history=store_history,
|
|
677
|
-
user_expressions=user_expressions or {},
|
|
678
|
-
allow_stdin=allow_stdin,
|
|
679
|
-
stop_on_error=False,
|
|
680
|
-
),
|
|
681
|
-
metadata=cell_meta,
|
|
676
|
+
return await self.shell.do_execute(
|
|
677
|
+
code=code,
|
|
678
|
+
silent=silent,
|
|
679
|
+
store_history=store_history,
|
|
680
|
+
user_expressions=user_expressions,
|
|
681
|
+
allow_stdin=allow_stdin,
|
|
682
|
+
cell_id=cell_id,
|
|
682
683
|
)
|
|
683
|
-
job: Job[ExecuteContent] = Job(msg=msg, ident=[], received_time=time.monotonic())
|
|
684
|
-
token = utils._job_var.set(job) # pyright: ignore[reportPrivateUsage]
|
|
685
|
-
try:
|
|
686
|
-
return await self.shell.execute_request(job)
|
|
687
|
-
finally:
|
|
688
|
-
utils._job_var.reset(token) # pyright: ignore[reportPrivateUsage]
|
|
689
684
|
|
|
690
685
|
def getpass(self, prompt="", stream=None) -> str:
|
|
691
686
|
"Matches signature of [ipykernel.kernelbase.Kernel.getpass][]."
|
|
@@ -16,10 +16,10 @@ from async_kernel import utils
|
|
|
16
16
|
from async_kernel.common import Fixed
|
|
17
17
|
from async_kernel.interface import HasInterface
|
|
18
18
|
from async_kernel.pending import PendingManager
|
|
19
|
-
from async_kernel.typing import
|
|
19
|
+
from async_kernel.typing import T_interface_co
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
-
from collections.abc import Callable, Generator
|
|
22
|
+
from collections.abc import Callable, Generator, Iterable
|
|
23
23
|
|
|
24
24
|
from async_kernel import Kernel
|
|
25
25
|
from async_kernel.typing import Content
|
|
@@ -207,23 +207,38 @@ class BaseShell(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_int
|
|
|
207
207
|
content["metadata"] = {}
|
|
208
208
|
self.parent.iopub_send("execute_result", content=content)
|
|
209
209
|
|
|
210
|
-
async def
|
|
211
|
-
|
|
210
|
+
async def do_execute(
|
|
211
|
+
self,
|
|
212
|
+
code: str = "",
|
|
213
|
+
*,
|
|
214
|
+
silent: bool = False,
|
|
215
|
+
store_history: bool = False,
|
|
216
|
+
user_expressions: dict[str, str] | None = None,
|
|
217
|
+
allow_stdin: bool = False,
|
|
218
|
+
stop_on_error: bool = False,
|
|
219
|
+
cell_id: str | None = None,
|
|
220
|
+
received_time: float = 0,
|
|
221
|
+
tags: Iterable[str] = (),
|
|
222
|
+
**_ignored,
|
|
223
|
+
) -> Content:
|
|
224
|
+
"""
|
|
225
|
+
Execute code in the shell.
|
|
226
|
+
"""
|
|
212
227
|
raise NotImplementedError
|
|
213
228
|
|
|
214
|
-
async def
|
|
215
|
-
""
|
|
229
|
+
async def do_complete(self, code: str, cursor_pos: int | None = None) -> Content:
|
|
230
|
+
""
|
|
216
231
|
raise NotImplementedError
|
|
217
232
|
|
|
218
|
-
async def
|
|
219
|
-
""
|
|
233
|
+
async def is_complete(self, code: str) -> Content:
|
|
234
|
+
""
|
|
220
235
|
raise NotImplementedError
|
|
221
236
|
|
|
222
|
-
async def
|
|
223
|
-
""
|
|
237
|
+
async def do_inspect(self, code: str, cursor_pos: int = 0, detail_level: Literal[0, 1] = 0) -> Content:
|
|
238
|
+
""
|
|
224
239
|
raise NotImplementedError
|
|
225
240
|
|
|
226
|
-
async def
|
|
241
|
+
async def do_history(
|
|
227
242
|
self,
|
|
228
243
|
*,
|
|
229
244
|
output: bool = False,
|
|
@@ -237,5 +252,5 @@ class BaseShell(HasInterface[T_interface_co], LoggingConfigurable, Generic[T_int
|
|
|
237
252
|
unique: bool = False,
|
|
238
253
|
**_ignored,
|
|
239
254
|
) -> Content:
|
|
240
|
-
""
|
|
255
|
+
""
|
|
241
256
|
raise NotImplementedError
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import builtins
|
|
8
8
|
import json
|
|
9
|
+
import math
|
|
9
10
|
import pathlib
|
|
10
11
|
import shlex
|
|
11
12
|
import sys
|
|
@@ -15,11 +16,10 @@ import weakref
|
|
|
15
16
|
from collections.abc import Callable
|
|
16
17
|
from contextlib import contextmanager
|
|
17
18
|
from sqlite3 import OperationalError
|
|
18
|
-
from typing import TYPE_CHECKING, Any, Literal, Never, Self, TextIO
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Literal, Never, Self, TextIO
|
|
19
20
|
|
|
20
21
|
import anyio
|
|
21
22
|
import IPython.core.release
|
|
22
|
-
from aiologic.lowlevel import async_checkpoint
|
|
23
23
|
from anyio.streams.text import TextReceiveStream
|
|
24
24
|
from IPython.core.completer import provisionalcompleter, rectify_completions
|
|
25
25
|
from IPython.core.displayhook import DisplayHook
|
|
@@ -46,10 +46,10 @@ from async_kernel.compiler import XCachingCompiler
|
|
|
46
46
|
from async_kernel.event_loop.run import get_runtime_matplotlib_guis
|
|
47
47
|
from async_kernel.interface.base import BaseInterface, HasInterface
|
|
48
48
|
from async_kernel.shell.base import BaseShell
|
|
49
|
-
from async_kernel.typing import Content,
|
|
49
|
+
from async_kernel.typing import Content, RunMode, Tags
|
|
50
50
|
|
|
51
51
|
if TYPE_CHECKING:
|
|
52
|
-
from collections.abc import Callable
|
|
52
|
+
from collections.abc import Callable, Iterable
|
|
53
53
|
from types import CodeType
|
|
54
54
|
|
|
55
55
|
from anyio.abc import ByteReceiveStream
|
|
@@ -379,7 +379,7 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
379
379
|
tempdirs = Fixed(list, mode="ignore")
|
|
380
380
|
|
|
381
381
|
_main_mod_cache = Fixed(dict)
|
|
382
|
-
_stop_on_error_pool: Fixed[Self, set[
|
|
382
|
+
_stop_on_error_pool: Fixed[Self, set[Pending[Any]]] = Fixed(set)
|
|
383
383
|
|
|
384
384
|
# Disabled attributes
|
|
385
385
|
loop_runner_map = None
|
|
@@ -641,106 +641,120 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
641
641
|
)
|
|
642
642
|
|
|
643
643
|
@override
|
|
644
|
-
async def
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
644
|
+
async def run_cell_async(
|
|
645
|
+
self,
|
|
646
|
+
raw_cell: str,
|
|
647
|
+
store_history=False,
|
|
648
|
+
silent=False,
|
|
649
|
+
shell_futures=True,
|
|
650
|
+
*,
|
|
651
|
+
transformed_cell: str | None = None,
|
|
652
|
+
preprocessing_exc_tuple: Any = None,
|
|
653
|
+
cell_id=None,
|
|
654
|
+
) -> ExecutionResult:
|
|
655
|
+
token = utils._cell_id_var.set(cell_id) # pyright: ignore[reportPrivateUsage]
|
|
656
|
+
result = None
|
|
657
|
+
try:
|
|
658
|
+
result = await super().run_cell_async(
|
|
659
|
+
raw_cell=raw_cell,
|
|
660
|
+
store_history=store_history,
|
|
661
|
+
silent=silent,
|
|
662
|
+
shell_futures=shell_futures,
|
|
663
|
+
transformed_cell=transformed_cell,
|
|
664
|
+
preprocessing_exc_tuple=preprocessing_exc_tuple,
|
|
665
|
+
cell_id=cell_id,
|
|
666
|
+
)
|
|
667
|
+
return result # noqa: RET504
|
|
668
|
+
finally:
|
|
669
|
+
self.events.trigger("post_execute")
|
|
670
|
+
if not silent:
|
|
671
|
+
self.events.trigger("post_run_cell", result)
|
|
672
|
+
utils._cell_id_var.reset(token) # pyright: ignore[reportPrivateUsage]
|
|
652
673
|
|
|
653
|
-
|
|
674
|
+
@override
|
|
675
|
+
async def do_execute(
|
|
676
|
+
self,
|
|
677
|
+
code: str = "",
|
|
678
|
+
*,
|
|
679
|
+
silent: bool = False,
|
|
680
|
+
store_history: bool = False,
|
|
681
|
+
user_expressions: dict[str, str] | None = None,
|
|
682
|
+
allow_stdin: bool = False,
|
|
683
|
+
stop_on_error: bool = False,
|
|
684
|
+
cell_id: str | None = None,
|
|
685
|
+
received_time: float = 0,
|
|
686
|
+
tags: Iterable[str] = (),
|
|
687
|
+
**_ignored,
|
|
688
|
+
) -> Content:
|
|
689
|
+
"""
|
|
690
|
+
Execute code in the shell's user_ns and global_ns.
|
|
691
|
+
"""
|
|
692
|
+
if received_time > 0 and (received_time < self._stop_on_error_info.get("time", 0)) and not silent:
|
|
654
693
|
return utils.error_to_content(RuntimeError("Aborting due to prior exception")) | {
|
|
655
694
|
"execution_count": self._stop_on_error_info.get("execution_count", 0)
|
|
656
695
|
}
|
|
657
|
-
|
|
696
|
+
if math.isnan(timeout := utils.get_tag_value(Tags.timeout, math.nan, tags=tags)):
|
|
697
|
+
timeout = self.timeout
|
|
698
|
+
|
|
699
|
+
if Tags.stop_on_error in tags:
|
|
700
|
+
stop_on_error = utils.get_tag_value(Tags.stop_on_error, stop_on_error, tags=tags)
|
|
701
|
+
elif Tags.raises_exception in tags or timeout:
|
|
702
|
+
stop_on_error = False
|
|
703
|
+
|
|
704
|
+
if silent:
|
|
705
|
+
execution_count: int = self.execution_count
|
|
706
|
+
else:
|
|
707
|
+
execution_count = self._execution_count = self._execution_count + 1
|
|
708
|
+
self.parent.iopub_send(
|
|
709
|
+
msg_or_type="execute_input",
|
|
710
|
+
content={"code": code, "execution_count": execution_count},
|
|
711
|
+
ident=b"kernel.execute_input",
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
pen = Caller().call_soon(
|
|
715
|
+
self.run_cell_async,
|
|
716
|
+
raw_cell=code,
|
|
717
|
+
store_history=store_history,
|
|
718
|
+
silent=silent,
|
|
719
|
+
transformed_cell=self.transform_cell_async(code),
|
|
720
|
+
shell_futures=True,
|
|
721
|
+
cell_id=cell_id,
|
|
722
|
+
)
|
|
723
|
+
err = result = None
|
|
658
724
|
try:
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
if
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
if stop_on_error:
|
|
689
|
-
self._stop_on_error_pool.add(cancel)
|
|
690
|
-
with anyio.fail_after(delay=timeout or None):
|
|
691
|
-
result = await self.run_cell_async(
|
|
692
|
-
raw_cell=code,
|
|
693
|
-
store_history=content.get("store_history", False),
|
|
694
|
-
silent=silent,
|
|
695
|
-
transformed_cell=self.transform_cell_async(code),
|
|
696
|
-
shell_futures=True,
|
|
697
|
-
cell_id=cell_id,
|
|
698
|
-
)
|
|
699
|
-
except (Exception, anyio.get_cancelled_exc_class()) as e:
|
|
700
|
-
# A safeguard to catch exceptions not caught by the shell.
|
|
701
|
-
if utils.LAUNCHED_BY_DEBUGPY_PYTEST:
|
|
702
|
-
raise
|
|
703
|
-
err = KernelInterrupt() if self.parent.last_interrupt_frame else e
|
|
704
|
-
else:
|
|
705
|
-
err = result.error_before_exec or result.error_in_exec if result else KernelInterrupt()
|
|
706
|
-
if not err and Tags.raises_exception in tags:
|
|
707
|
-
msg = "An expected exception was not raised!"
|
|
708
|
-
err = RuntimeError(msg)
|
|
709
|
-
finally:
|
|
710
|
-
self._stop_on_error_pool.discard(cancel)
|
|
711
|
-
self.kernel.interrupts.discard(cancel)
|
|
712
|
-
self.events.trigger("post_execute")
|
|
713
|
-
if not silent:
|
|
714
|
-
self.events.trigger("post_run_cell", result)
|
|
715
|
-
if (err) and (isinstance(err, anyio.get_cancelled_exc_class()) and (timeout != 0)):
|
|
716
|
-
# Suppress the error due to either:
|
|
717
|
-
# 1. tag
|
|
718
|
-
# 2. timeout
|
|
719
|
-
err = None
|
|
720
|
-
content = {
|
|
721
|
-
"status": "error" if err else "ok",
|
|
722
|
-
"execution_count": execution_count,
|
|
723
|
-
"user_expressions": self.user_expressions(content.get("user_expressions", {})),
|
|
724
|
-
}
|
|
725
|
-
if err:
|
|
726
|
-
content |= utils.error_to_content(err)
|
|
727
|
-
if (not silent) and stop_on_error:
|
|
728
|
-
with anyio.CancelScope(shield=True):
|
|
729
|
-
await async_checkpoint(force=True)
|
|
730
|
-
self._stop_on_error_info["time"] = time.monotonic() + float(self.stop_on_error_time_offset)
|
|
731
|
-
self._stop_on_error_info["execution_count"] = execution_count
|
|
732
|
-
self.log.info("An error occurred in a non-silent execution request")
|
|
733
|
-
if stop_on_error:
|
|
734
|
-
for c in frozenset(self._stop_on_error_pool):
|
|
735
|
-
c()
|
|
736
|
-
await async_checkpoint(force=True)
|
|
737
|
-
return content
|
|
738
|
-
finally:
|
|
739
|
-
utils._cell_id_var.reset(token) # pyright: ignore[reportPrivateUsage]
|
|
725
|
+
self.kernel.active_execute_requests.add(pen)
|
|
726
|
+
pen.add_done_callback(self.kernel.active_execute_requests.discard)
|
|
727
|
+
if stop_on_error:
|
|
728
|
+
self._stop_on_error_pool.add(pen)
|
|
729
|
+
pen.add_done_callback(self._stop_on_error_pool.discard)
|
|
730
|
+
result = await pen.wait(timeout=timeout or None)
|
|
731
|
+
except Exception as e:
|
|
732
|
+
err = KernelInterrupt() if str(e) == self.kernel._interrupt_message else e # pyright: ignore[reportPrivateUsage]
|
|
733
|
+
else:
|
|
734
|
+
err = result.error_before_exec or result.error_in_exec if result else KernelInterrupt()
|
|
735
|
+
if not err and Tags.raises_exception in tags:
|
|
736
|
+
msg = "An expected exception was not raised!"
|
|
737
|
+
err = RuntimeError(msg)
|
|
738
|
+
|
|
739
|
+
content = {
|
|
740
|
+
"status": "error" if err else "ok",
|
|
741
|
+
"execution_count": execution_count,
|
|
742
|
+
"user_expressions": self.user_expressions(user_expressions if user_expressions is not None else {}),
|
|
743
|
+
}
|
|
744
|
+
if err:
|
|
745
|
+
content |= utils.error_to_content(err)
|
|
746
|
+
if (not silent) and stop_on_error:
|
|
747
|
+
self._stop_on_error_info["time"] = time.monotonic() + float(self.stop_on_error_time_offset)
|
|
748
|
+
self._stop_on_error_info["execution_count"] = execution_count
|
|
749
|
+
self.log.info("An error occurred in %s %s", self, pen)
|
|
750
|
+
if stop_on_error:
|
|
751
|
+
for pen in tuple(self._stop_on_error_pool):
|
|
752
|
+
pen.cancel("Stop on error cancellation")
|
|
753
|
+
return content
|
|
740
754
|
|
|
741
755
|
@override
|
|
742
|
-
async def
|
|
743
|
-
""
|
|
756
|
+
async def do_complete(self, code: str, cursor_pos: int | None = None) -> Content:
|
|
757
|
+
""
|
|
744
758
|
|
|
745
759
|
cursor_pos = cursor_pos or len(code)
|
|
746
760
|
with provisionalcompleter():
|
|
@@ -767,8 +781,8 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
767
781
|
}
|
|
768
782
|
|
|
769
783
|
@override
|
|
770
|
-
async def
|
|
771
|
-
""
|
|
784
|
+
async def is_complete(self, code: str) -> Content:
|
|
785
|
+
""
|
|
772
786
|
status, indent_spaces = self.input_transformer_manager.check_complete(code)
|
|
773
787
|
content = {"status": status}
|
|
774
788
|
if status == "incomplete":
|
|
@@ -776,8 +790,8 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
776
790
|
return content
|
|
777
791
|
|
|
778
792
|
@override
|
|
779
|
-
async def
|
|
780
|
-
""
|
|
793
|
+
async def do_inspect(self, code: str, cursor_pos: int = 0, detail_level: Literal[0, 1] = 0) -> Content:
|
|
794
|
+
""
|
|
781
795
|
content = {"data": {}, "metadata": {}, "found": True}
|
|
782
796
|
try:
|
|
783
797
|
oname = token_at_cursor(code, cursor_pos)
|
|
@@ -788,7 +802,7 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
788
802
|
return content
|
|
789
803
|
|
|
790
804
|
@override
|
|
791
|
-
async def
|
|
805
|
+
async def do_history(
|
|
792
806
|
self,
|
|
793
807
|
*,
|
|
794
808
|
output: bool = False,
|
|
@@ -802,7 +816,7 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
802
816
|
unique: bool = False,
|
|
803
817
|
**_ignored,
|
|
804
818
|
) -> Content:
|
|
805
|
-
""
|
|
819
|
+
""
|
|
806
820
|
history_manager = self.history_manager
|
|
807
821
|
assert history_manager
|
|
808
822
|
match hist_access_type:
|
|
@@ -818,7 +832,7 @@ class IPShell(BaseShell, InteractiveShell): # pyright: ignore[reportUnsafeMulti
|
|
|
818
832
|
|
|
819
833
|
@override
|
|
820
834
|
def _showtraceback(self, etype, evalue, stb) -> None:
|
|
821
|
-
if
|
|
835
|
+
if self.timeout != 0.0 and etype is anyio.get_cancelled_exc_class():
|
|
822
836
|
etype, evalue, stb = TimeoutError, "Cell execute timeout", []
|
|
823
837
|
if isinstance(evalue, KernelInterrupt):
|
|
824
838
|
stb = []
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import math
|
|
4
3
|
import sys
|
|
5
4
|
import threading
|
|
6
5
|
import traceback
|
|
@@ -13,15 +12,14 @@ from traitlets import traitlets
|
|
|
13
12
|
from typing_extensions import TypeVar
|
|
14
13
|
|
|
15
14
|
import async_kernel.interface
|
|
16
|
-
from async_kernel.typing import Tags
|
|
17
15
|
|
|
18
16
|
if TYPE_CHECKING:
|
|
19
|
-
from collections.abc import Generator, Mapping
|
|
17
|
+
from collections.abc import Generator, Iterable, Mapping
|
|
20
18
|
from contextlib import _SupportsRedirect, _SupportsRedirectT # pyright: ignore[reportPrivateUsage]
|
|
21
19
|
|
|
22
20
|
from async_kernel.kernel import Kernel
|
|
23
21
|
from async_kernel.shell import BaseShell
|
|
24
|
-
from async_kernel.typing import Content, Job, Message
|
|
22
|
+
from async_kernel.typing import Content, Job, Message, Tags
|
|
25
23
|
|
|
26
24
|
__all__ = [
|
|
27
25
|
"apply_settings",
|
|
@@ -34,7 +32,6 @@ __all__ = [
|
|
|
34
32
|
"get_subshell_id",
|
|
35
33
|
"get_tag_value",
|
|
36
34
|
"get_tags",
|
|
37
|
-
"get_timeout",
|
|
38
35
|
"mark_thread_pydev_do_not_trace",
|
|
39
36
|
"redirect_stderr",
|
|
40
37
|
"redirect_stdout",
|
|
@@ -134,7 +131,7 @@ def get_cell_id(job: Job | None = None, /) -> str | None:
|
|
|
134
131
|
_TagType = TypeVar("_TagType", str, float, int, bool)
|
|
135
132
|
|
|
136
133
|
|
|
137
|
-
def get_tag_value(tag: Tags, default: _TagType, /, *, tags:
|
|
134
|
+
def get_tag_value(tag: Tags, default: _TagType, /, *, tags: Iterable[str] | None = None) -> _TagType:
|
|
138
135
|
"""
|
|
139
136
|
Get the value for the tag from a collection of tags.
|
|
140
137
|
|
|
@@ -158,13 +155,6 @@ def get_tag_value(tag: Tags, default: _TagType, /, *, tags: list[str] | None = N
|
|
|
158
155
|
return default
|
|
159
156
|
|
|
160
157
|
|
|
161
|
-
def get_timeout(*, tags: list[str] | None = None) -> float:
|
|
162
|
-
"Gets the timeout from tags or using the current context."
|
|
163
|
-
if math.isnan(timeout := get_tag_value(Tags.timeout, math.nan, tags=tags)):
|
|
164
|
-
return get_kernel().shell.timeout
|
|
165
|
-
return max(timeout, 0.0)
|
|
166
|
-
|
|
167
|
-
|
|
168
158
|
def setattr_nested(obj: object, name: str, value: str | Any, *, _return_value=False) -> dict[str, Any]:
|
|
169
159
|
"""
|
|
170
160
|
Replace an existing nested attribute/trait of an object.
|
|
@@ -12,6 +12,7 @@ import async_kernel
|
|
|
12
12
|
from async_kernel.compat.json import pack_json_str, unpack_json
|
|
13
13
|
from async_kernel.interface import start_kernel_callable_interface
|
|
14
14
|
from async_kernel.interface.callable import CallableInterface
|
|
15
|
+
from tests import utils
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from async_kernel.typing import Message
|
|
@@ -92,3 +93,24 @@ class TestCallableInterface:
|
|
|
92
93
|
async def test_keyboard_interrupt(self, interface) -> None:
|
|
93
94
|
with pytest.raises(KeyboardInterrupt):
|
|
94
95
|
signal.raise_signal(signal.SIGINT)
|
|
96
|
+
|
|
97
|
+
async def test_kernel_interrupt_pending(self, interface: CallableInterface):
|
|
98
|
+
interface.kernel.shell.user_ns["ready_event"] = ready = Event()
|
|
99
|
+
code = "ready_event.set()\nimport anyio\nawait anyio.sleep_forever()"
|
|
100
|
+
pen = interface.kernel.caller.call_soon(interface.kernel.do_execute, code, silent=False)
|
|
101
|
+
await ready
|
|
102
|
+
interface.kernel.do_interrupt()
|
|
103
|
+
result = await pen
|
|
104
|
+
assert result
|
|
105
|
+
|
|
106
|
+
async def test_kernel_interrupt_keyboard(self, interface: CallableInterface):
|
|
107
|
+
interface.kernel.shell.user_ns["ready_event"] = ready = Event()
|
|
108
|
+
|
|
109
|
+
async def run_code():
|
|
110
|
+
code = f"ready_event.set()\nimport time\ntime.sleep({utils.TIMEOUT * 2})"
|
|
111
|
+
pen = interface.kernel.caller.call_soon(interface.kernel.do_execute, code, silent=False)
|
|
112
|
+
await ready
|
|
113
|
+
interface.kernel.do_interrupt()
|
|
114
|
+
await pen.wait(result=False)
|
|
115
|
+
|
|
116
|
+
await interface.kernel.caller.to_thread(run_code)
|
|
@@ -38,14 +38,9 @@ async def test_execute_shell_timeout(client: AsyncKernelClient, kernel: Kernel,
|
|
|
38
38
|
last_stop_time = kernel.shell._stop_on_error_info
|
|
39
39
|
try:
|
|
40
40
|
code = "\n".join(["import anyio", "await anyio.sleep_forever()"])
|
|
41
|
-
|
|
41
|
+
_, content = await utils.execute(client, code=code, metadata=metadata, clear_pub=False)
|
|
42
42
|
assert last_stop_time == kernel.shell._stop_on_error_info, "Should not cause cancellation"
|
|
43
|
-
assert content["status"] == "
|
|
44
|
-
await utils.check_pub_message(client, msg_id, execution_state="busy")
|
|
45
|
-
await utils.check_pub_message(client, msg_id, msg_type="execute_input")
|
|
46
|
-
expected = {"traceback": [], "ename": "TimeoutError", "evalue": "Cell execute timeout"}
|
|
47
|
-
await utils.check_pub_message(client, msg_id, msg_type="error", **expected)
|
|
48
|
-
await utils.check_pub_message(client, msg_id, execution_state="idle")
|
|
43
|
+
assert content["status"] == "error"
|
|
49
44
|
finally:
|
|
50
45
|
kernel.shell.timeout = 0.0
|
|
51
46
|
|
|
@@ -88,7 +83,7 @@ async def test_save_history(client: AsyncKernelClient, tmp_path):
|
|
|
88
83
|
("%%timeit\na\n\n", "complete"),
|
|
89
84
|
],
|
|
90
85
|
)
|
|
91
|
-
async def
|
|
86
|
+
async def test_is_complete_2(client: AsyncKernelClient, code: str, status: str):
|
|
92
87
|
# There are more test cases for this in core - here we just check
|
|
93
88
|
# that the kernel exposes the interface correctly.
|
|
94
89
|
client.is_complete(code)
|
|
@@ -141,7 +141,7 @@ async def test_execute_stop_on_error_task(client: AsyncKernelClient):
|
|
|
141
141
|
assert content.get("status") == "error"
|
|
142
142
|
assert "ValueError" in "".join(content["traceback"])
|
|
143
143
|
content = await utils.get_shell_message(client, msg_id_1, "execute_reply")
|
|
144
|
-
assert "
|
|
144
|
+
assert "Stop on error cancellation" in "".join(content["traceback"])
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
async def test_user_expressions(client: AsyncKernelClient):
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import pathlib
|
|
4
|
-
import threading
|
|
5
4
|
from typing import TYPE_CHECKING, Any, Literal
|
|
6
5
|
|
|
7
6
|
import anyio
|
|
8
7
|
import pytest
|
|
9
8
|
|
|
9
|
+
from async_kernel import Pending
|
|
10
10
|
from async_kernel.interface.zmq import ZMQInterface
|
|
11
11
|
from async_kernel.typing import MsgType
|
|
12
12
|
from tests import utils
|
|
@@ -87,12 +87,12 @@ async def test_input(
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
async def test_interrupt_request(client: AsyncKernelClient, kernel: Kernel):
|
|
90
|
-
|
|
91
|
-
kernel.
|
|
90
|
+
pen: Any = Pending()
|
|
91
|
+
kernel.active_execute_requests.add(pen)
|
|
92
92
|
reply = await utils.send_control_message(client, MsgType.interrupt_request)
|
|
93
93
|
assert reply["header"]["msg_type"] == "interrupt_reply"
|
|
94
94
|
assert reply["content"] == {"status": "ok"}
|
|
95
|
-
assert
|
|
95
|
+
assert pen.cancelled()
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
async def test_interrupt_request_async_request(subprocess_kernels_client: AsyncKernelClient):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|