AnyIO

raw JSON →
4.13.0 verified Tue May 12 auth: no python install: verified quickstart: verified

AnyIO is a high-level asynchronous networking and concurrency library that runs on top of either asyncio or Trio, implementing Trio-like structured concurrency on both backends. Code written against AnyIO's API runs unmodified on either backend, allowing incremental adoption. Current stable version is 4.13.0, with an active monthly-ish release cadence under Alex Grönholm (agronholm).

pip install anyio
error ModuleNotFoundError: No module named 'anyio'
cause The 'anyio' package is not installed in the Python environment.
fix
Install the 'anyio' package using pip: 'pip install anyio'.
error ImportError: cannot import name 'start_blocking_portal' from 'anyio'
cause The 'start_blocking_portal' function is not available in the installed version of 'anyio'.
fix
Upgrade 'anyio' to the latest version using pip: 'pip install --upgrade anyio'.
error AttributeError: module 'anyio' has no attribute 'start_blocking_portal'
cause The 'start_blocking_portal' function is not present in the installed version of 'anyio'.
fix
Ensure you have the latest version of 'anyio' installed by running: 'pip install --upgrade anyio'.
error ModuleNotFoundError: No module named 'anyio._backends'
cause The 'anyio._backends' module is missing, possibly due to packaging issues.
fix
Ensure all necessary modules are included during packaging, especially when using tools like PyInstaller.
error RuntimeError: An attempt was made to start a new application while another is still running
cause This error occurs when trying to call `anyio.run()` (or `asyncio.run()`) from within a thread where an asynchronous event loop is already active. AnyIO's `run()` function is designed to start an event loop, not to be called from inside one.
fix
If you are already inside an async context, use anyio.create_task_group().start_soon() or anyio.to_thread.run_sync() for blocking calls, instead of anyio.run(). If you need to run async code from a synchronous thread not managed by AnyIO, use anyio.from_thread.run() or anyio.from_thread.start_blocking_portal().
breaking In AnyIO 4, task groups always raise ExceptionGroup (PEP 654) when any child task raises. The old anyio.ExceptionGroup class was removed. Code catching exceptions from task groups must use 'except*' syntax (Python 3.11+) or the 'exceptiongroup' backport on older Pythons.
fix Use 'except* ValueError as eg:' on Python 3.11+, or install the 'exceptiongroup' package and import ExceptionGroup from it on Python 3.10.
breaking TaskGroup.spawn() was removed in AnyIO 4. Calling it raises AttributeError.
fix Replace 'await tg.spawn(fn, arg)' with the synchronous 'tg.start_soon(fn, arg)' (no await).
breaking start_blocking_portal() was moved to anyio.from_thread and must be used as a synchronous context manager. Importing it from the top-level anyio namespace raises ImportError.
fix Use: from anyio.from_thread import start_blocking_portal with start_blocking_portal() as portal: portal.call(fn)
deprecated The 'item_type' keyword argument to create_memory_object_stream() is deprecated and will be removed in a future major version.
fix Use the generic syntax: create_memory_object_stream[MyType](max_buffer_size=N)
deprecated The 'cancellable' parameter of to_thread.run_sync() is a deprecated alias for 'abandon_on_cancel'. Passing it emits a DeprecationWarning.
fix Replace cancellable=True with abandon_on_cancel=True.
gotcha anyio.Path cannot be substituted for pathlib.Path. APIs expecting a pathlib.Path will reject an anyio.Path instance; convert with str(path) or pass the underlying pathlib.Path via path._path (private, unstable).
fix Pass str(anyio_path) to third-party APIs expecting pathlib.Path, or open the file with await anyio_path.open() and hand the file object over.
gotcha Mixing native asyncio tasks (asyncio.create_task) with AnyIO cancel scopes can cause tasks spawned outside AnyIO to escape cancellation, and await inside a finally block can receive multiple CancelledError instances under scoped cancellation.
fix Spawn all concurrent work through anyio.create_task_group().start_soon() and avoid bare asyncio.create_task() inside AnyIO-managed code.
pip install anyio[trio]
pip install anyio[trio] pytest-anyio
python os / libc variant status wheel install import disk
3.10 alpine (musl) anyio - - 0.17s 20.0M
3.10 alpine (musl) trio - - 0.17s 24.8M
3.10 alpine (musl) trio - - 0.17s 37.1M
3.10 slim (glibc) anyio - - 0.11s 21M
3.10 slim (glibc) trio - - 0.11s 25M
3.10 slim (glibc) trio - - 0.12s 38M
3.11 alpine (musl) anyio - - 0.28s 22.0M
3.11 alpine (musl) trio - - 0.28s 28.0M
3.11 alpine (musl) trio - - 0.28s 41.8M
3.11 slim (glibc) anyio - - 0.23s 22M
3.11 slim (glibc) trio - - 0.24s 28M
3.11 slim (glibc) trio - - 0.23s 42M
3.12 alpine (musl) anyio - - 0.47s 13.8M
3.12 alpine (musl) trio - - 0.49s 19.5M
3.12 alpine (musl) trio - - 0.50s 33.0M
3.12 slim (glibc) anyio - - 0.44s 14M
3.12 slim (glibc) trio - - 0.47s 20M
3.12 slim (glibc) trio - - 0.46s 33M
3.13 alpine (musl) anyio - - 0.20s 13.1M
3.13 alpine (musl) trio - - 0.20s 18.8M
3.13 alpine (musl) trio - - 0.20s 32.3M
3.13 slim (glibc) anyio - - 0.20s 14M
3.13 slim (glibc) trio - - 0.20s 19M
3.13 slim (glibc) trio - - 0.20s 33M
3.9 alpine (musl) anyio - - 0.15s 19.4M
3.9 alpine (musl) trio - - 0.15s 24.2M
3.9 alpine (musl) trio - - 0.16s 36.3M
3.9 slim (glibc) anyio - - 0.14s 20M
3.9 slim (glibc) trio - - 0.13s 25M
3.9 slim (glibc) trio - - 0.14s 37M

Demonstrates concurrent task groups, move_on_after timeout scopes, and offloading blocking calls to a worker thread — all backend-agnostic.

import anyio

async def fetch(name: str, delay: float) -> None:
    print(f'{name}: starting')
    await anyio.sleep(delay)
    print(f'{name}: done after {delay}s')

async def main() -> None:
    # Task group: all tasks run concurrently; exceptions surface as ExceptionGroup
    async with anyio.create_task_group() as tg:
        tg.start_soon(fetch, 'task-A', 1.0)
        tg.start_soon(fetch, 'task-B', 0.5)

    # Timeout via CancelScope
    with anyio.move_on_after(2.0) as scope:
        await anyio.sleep(5)
    if scope.cancelled_caught:
        print('timed out (moved on)')

    # Run a blocking call in a thread
    import time
    result = await anyio.to_thread.run_sync(time.strftime, '%X')
    print('wall-clock time from thread:', result)

if __name__ == '__main__':
    # Default backend is asyncio; swap to 'trio' with no code changes
    anyio.run(main, backend='asyncio')