AnyIO
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).
Warnings
- 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.
- breaking TaskGroup.spawn() was removed in AnyIO 4. Calling it raises AttributeError.
- 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.
- deprecated The 'item_type' keyword argument to create_memory_object_stream() is deprecated and will be removed in a future major version.
- deprecated The 'cancellable' parameter of to_thread.run_sync() is a deprecated alias for 'abandon_on_cancel'. Passing it emits a DeprecationWarning.
- 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).
- 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.
Install
-
pip install anyio -
pip install anyio[trio] -
pip install anyio[trio] pytest-anyio
Imports
- run
import anyio anyio.run(main)
- create_task_group
from anyio import create_task_group async with create_task_group() as tg: tg.start_soon(fn) - create_memory_object_stream
from anyio import create_memory_object_stream send, recv = create_memory_object_stream[int](max_buffer_size=10)
- start_blocking_portal
from anyio.from_thread import start_blocking_portal with start_blocking_portal() as portal: portal.call(async_fn) - to_thread.run_sync
from anyio import to_thread await to_thread.run_sync(blocking_fn, abandon_on_cancel=True)
- CancelScope
from anyio import CancelScope with CancelScope(deadline=...) as scope: ... - Path
from anyio import Path path = Path('/tmp/file') content = await path.read_text() - get_cancelled_exc_class
from anyio import get_cancelled_exc_class try: await something() except get_cancelled_exc_class(): raise
Quickstart
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')