Metadata-Version: 2.4
Name: async-kernel
Version: 0.16.1
Summary: A concurrent python kernel for Jupyter supporting AnyIO, AsyncIO and Trio.
Project-URL: Homepage, https://fleming79.github.io/async-kernel
Project-URL: Documentation, https://fleming79.github.io/async-kernel
Project-URL: Source, https://github.com/fleming79/async-kernel
Project-URL: Tracker, https://github.com/fleming79/async-kernel/issues
Project-URL: Changelog, https://fleming79.github.io/async-kernel/latest/about/changelog/
Author-email: Alan Fleming <async-python@proton.me>
License-Expression: MIT
License-File: IPYTHON_LICENSE
License-File: LICENSE
Keywords: Interactive,Interpreter,Jupyter,Shell,Web
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AnyIO
Classifier: Framework :: AsyncIO
Classifier: Framework :: Jupyter
Classifier: Framework :: Jupyter :: JupyterLab
Classifier: Framework :: Jupyter :: JupyterLab :: 4
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
Classifier: Framework :: Trio
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Free Threading :: 1 - Unstable
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: aiologic>=0.16.0
Requires-Dist: anyio>=4.12
Requires-Dist: comm>=0.2
Requires-Dist: ipython>=9.0
Requires-Dist: jupyter-client>=8.8; sys_platform != 'emscripten'
Requires-Dist: jupyter-core>=5.9.1
Requires-Dist: matplotlib-inline>0.1
Requires-Dist: orjson>=3.10.16; sys_platform == 'emscripten'
Requires-Dist: outcome; sys_platform != 'emscripten'
Requires-Dist: pyzmq>=27.0; sys_platform != 'emscripten'
Requires-Dist: sniffio>=1.3.0; sys_platform != 'emscripten'
Requires-Dist: traitlets>=5.14
Requires-Dist: typing-extensions>=4.14
Requires-Dist: wrapt>=2.0.1
Description-Content-Type: text/markdown

# async-kernel

[![pypi](https://img.shields.io/pypi/pyversions/async-kernel.svg)](https://pypi.python.org/pypi/async-kernel)
[![downloads](https://img.shields.io/pypi/dm/async-kernel?logo=pypi&color=3775A9)](https://pypistats.org/packages/async-kernel)
[![CI](https://github.com/fleming79/async-kernel/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fleming79/async-kernel/actions/workflows/ci.yml)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![basedpyright - checked](https://img.shields.io/badge/basedpyright-checked-42b983)](https://docs.basedpyright.com)
[![Built with Material for MkDocs](https://img.shields.io/badge/Material_for_MkDocs-526CFE?style=plastic&logo=MaterialForMkDocs&logoColor=white)](https://squidfunk.github.io/mkdocs-material/)
[![codecov](https://codecov.io/github/fleming79/async-kernel/graph/badge.svg?token=PX0RWNKT85)](https://codecov.io/github/fleming79/async-kernel)

![logo-svg](https://github.com/user-attachments/assets/6781ec08-94e9-4640-b8f9-bb07a08e9587)

async-kernel is a Python kernel for [Jupyter](https://docs.jupyter.org/en/latest/projects/kernels.html#kernels-programming-languages)
that provides concurrent message handling via an asynchronous backend (asyncio or trio).

The kernel provides two external interfaces:

1. Direct ZMQ socket messaging via a configuration file and kernel spec - (Jupyter, VS Code, etc).
2. An experimental callback style interface (Jupyterlite).

## Highlights

- [IPython shell](https://ipython.readthedocs.io/en/stable/overview.html#enhanced-interactive-python-shell)
    - top-level await ('asyncio' or 'trio' backend) in cells
- [anyio](https://pypi.org/project/anyio/) compatible asynchronous backend ([`asyncio`](https://docs.python.org/3/library/asyncio.html) (default) or [`trio`](https://pypi.org/project/trio/))
- [aiologic](https://aiologic.readthedocs.io/latest/) thread-safe synchronisation primitives
- [Backend agnostic multi-thread / multi-event loop management](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller)
- Per-subshell user_ns
- GUI event loops [^1]
    - [x] inline
    - [x] ipympl
    - [x] tk host and asyncio[^2] or trio[^3] backend running as a guest
    - [x] qt host and asyncio[^2] or trio[^3] backend running as a guest
- [Experimental](https://github.com/fleming79/echo-kernel) support for
  [Jupyterlite](https://github.com/jupyterlite/jupyterlite) (try it online [here](https://fleming79.github.io/echo-kernel/) 👈)
- [Debugger client](https://jupyterlab.readthedocs.io/en/latest/user/debugger.html#debugger)

[^1]:
    A gui (_host_) enabled kernel interface starts a gui's mainloop (host) which starts
    the backend as a guest, then finally the Kernel is started.

[^2]:
    The asyncio implementation of `start_guest_run` was written by
    [the author of aiologic](https://github.com/x42005e1f/aiologic) and provided as a
    [gist](https://gist.github.com/x42005e1f/857dcc8b6865a11f1ffc7767bb602779).

[^3]: trio's [start_guest_run](https://trio.readthedocs.io/en/stable/reference-lowlevel.html#trio.lowlevel.start_guest_run).

### Avoid deadlocks

The standard (synchronous) kernel implementation processes messages sequentially irrespective
of the message type. The problem being that long running execute requests make the kernel non-responsive.

Another problem exists when an asynchronous execute request awaits a result that is delivered
via a kernel message - this will cause a deadlock because the message will be stuck in the queue behind
the _blocking_ execute request[^5].

async-kernel handles messages according to the channel and message type. So widget com message
will get processed in a separate queue to an execute request. Further detail is given in the [concurrency notebook](https://fleming79.github.io/async-kernel/latest/notebooks/concurrency/), a Jupyterlite version is available [here](https://fleming79.github.io/echo-kernel/).

#### Example

Make a blocking call in a Jupyter lab notebook or console.

```python
# Make the shell's thread busy
import time

time.sleep(1e6)
```

While the above is _blocking_ (the kernel is _busy_).

```python
dir()  # try code completion (tab) or view the docstring (shift tab)
```

Interrupt the kernel.

It also works for awaitables.

```python
import ipywidgets as ipw
from aiologic import Event

b = ipw.Button(description="Click me")
event = Event()
b.on_click(lambda _: event.set())
display(b)
await event
```

[^5]:
    IPyKernel _solves_ this issue specifically for widgets by using the concept of
    'widget coms over subshells'. Widget messages arrive in a different thread which on
    occasion can cause unexpected behaviour, especially when using asynchronous libraries.

## Installation

```bash
pip install async-kernel
```

## Kernel specs

A kernel spec with the name 'async' is added when async-kernel is installed.

Kernel specs can be added/removed via the command line.

### Backends

The backend set on the interface is the asynchronous library the kernel uses for message handling.
It is also the asynchronous library directly available when executing code in cells or via a console[^3].

[^3]:
    Irrespective of the configured backend, functions/coroutines can be executed using a specific backend
    with the method [`call_using_backend`](https://fleming79.github.io/async-kernel/latest/reference/caller/#async_kernel.caller.Caller.call_using_backend).

#### Example - overwrite the 'async' kernel spec to use a trio backend

```bash
pip install trio
async-kernel -a async --interface.backend=trio
```

### Gui event loop

The kernel can be started with a gui event loop as the _host_ and the _backend_ running as a guest.

#### asyncio backend

```bash
# tk
async-kernel -a async-tk --interface.host=tk

# qt
pip install PySide6-Essentials
async-kernel -a async-qt --interface.host=qt
```

#### trio backend

```bash
pip install trio
# tk
async-kernel -a async-tk --interface.host=tk --interface.backend=trio

# qt
pip install PySide6-Essentials
async-kernel -a async-qt --interface.host=qt --interface.backend=trio
```

For further detail about kernel spec customisation see [command line and kernel configuration](https://fleming79.github.io/async-kernel/latest/usage/commands/).

## Faster data serialization

[orjson](https://github.com/ijl/orjson) (a fast JSON library) is supported and will be used by default if it has been installed.

## Free-threading support

async-kernel's Caller's are _thread-local_ and it's methods are _internally synchronised_[^4].

[^4]: [free threading terminology](https://py-free-threading.github.io/documentation-principles/#free-threading-terminology)

## Origin

async-kernel started as a [fork](https://github.com/ipython/ipykernel/commit/8322a7684b004ee95f07b2f86f61e28146a5996d)
of [IPyKernel](https://github.com/ipython/ipykernel). Thank you to the original contributors of IPyKernel that made async-kernel possible.
