AnyIO Documentation

repository·master·Indexed 25 days ago

https://github.com/agronholm/anyio

A high-level concurrency and networking framework for Python that provides a unified API for both asyncio and Trio. AnyIO emphasizes structured concurrency through Task Groups and a level cancellation model using cancel scopes. It includes utilities for running synchronous code in worker threads or processes, asynchronous file I/O, stream-based data transfer, and various synchronization primitives.

Tokens
38.4K
Snippets
81
Records
216
Agent score
82%

What's inside anyio

  1. Overview of AnyIO

    master

    AnyIO is an asynchronous networking and concurrency library designed to work on top of either asyncio or Trio. It provides a unified API that allows applications and libraries to run unmodified on either backend.

    A key feature is its implementation of structured concurrency (SC) on top of asyncio, which mimics the native SC behavior found in Trio. This allows for incremental adoption into existing codebases without requiring a full refactoring.

  2. Why use AnyIO APIs instead of asyncio APIs

    master
    AnyIO provides a set of Trio-inspired APIs designed to improve upon asyncio. While asyncio is the standard library implementation, AnyIO addresses several design issues and missing features, particularly regarding structured concurrency and task management. Even for application development (not just library development), AnyIO offers more robust primitives for managing task lifecycles and synchronization.
  3. Use TLS streams for secure TCP connections

    master

    AnyIO supports TLS (Transport Layer Security) to provide authenticity and confidentiality for TCP streams. TLS is typically established immediately after a connection is made via a handshake that involves certificate exchange and hostname verification.

    To implement TLS, you can use TLSListener to wrap a standard TCP listener on the server side, or use the ssl_context argument when connecting via connect_tcp on the client side.

    import ssl
    from anyio import create_tcp_listener, run
    from anyio.streams.tls import TLSListener
    
    async def handle(client):
        async with client:
            name = await client.receive()
            await client.send(b'Hello, %s\n' % name)
    
    async def main():
        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        context.load_cert_chain(certfile='cert.pem', keyfile='key.pem')
        listener = TLSListener(await create_tcp_listener(local_port=1234), context)
        await listener.serve(handle)
    
    run(main)
  4. Implement context managers using AnyIO mix-in classes

    master
    AnyIO provides mix-in classes to help safely implement asynchronous context managers that embed other context managers. Instead of manually implementing __aenter__ and __aexit__ (which requires managing state and handling exceptions in __aenter__), you can use these mix-ins to write logic similar to @contextlib.asynccontextmanager. Note that using these mix-ins sacrifices re-entrancy.
  5. Control context propagation in spawned tasks

    master

    By default, when a task is spawned, the context (from contextvars) is copied to the new task.

    Important: The context copied is the context of the task that calls TaskGroup.start() or TaskGroup.start_soon(), not the context of the task group's host task.

    To run a task in a specific, custom context instead of inheriting the caller's context, pass a contextvars.Context object to the context keyword argument in TaskGroup.create_task().

  6. Use Connectables for stream production

    master
    To complement the stream class hierarchy, AnyIO provides Connectables. This abstraction allows you to produce connected streams (either object-oriented or bytes-oriented). This is particularly useful for network clients, as it allows you to abstract the connection mechanism, making it easier to customize or mock connections without using monkey patching.
  7. Avoid cancel scope stack corruption

    master

    Cancel scopes must be entered and exited in LIFO (Last-In, First-Out) order. Violating this can lead to stack corruption and unpredictable behavior.

    Risky patterns to avoid:

    • Manually calling __enter__ and __exit__ in the wrong order.
    • Using [Async]ExitStack in a way that doesn't mirror nested context managers.
    • Yielding in an async generator while enclosed in a cancel scope (this violates structural concurrency).

    Safe pattern for async context managers: It is generally safe to use task groups or cancel scopes within an @asynccontextmanager as long as the host task remains running throughout the entire lifecycle of the context manager.

    from contextlib import asynccontextmanager
    from anyio import create_task_group
    
    # Okay in most cases!
    @asynccontextmanager
    async def some_context_manager():
        async with create_task_group() as tg:
            tg.start_soon(foo)
            yield
  8. AnyIO Core Features

    master

    AnyIO provides a wide range of asynchronous primitives and networking capabilities:

    • Concurrency Control: Task groups (equivalent to Trio's nurseries) and worker threads.
    • Networking: High-level TCP, UDP, and UNIX sockets.
      • Includes the Happy eyeballs algorithm for more robust TCP connections.
      • Provides async/await style UDP sockets (avoiding the Transport/Protocol pattern required by asyncio).
    • Streams: Versatile APIs for both byte streams and object streams.
    • Synchronization & Communication: Locks, conditions, events, semaphores, and object streams.
    • System & Parallelization:
      • Subprocesses.
      • Subinterpreter support for code parallelization (Python 3.13+).
      • Asynchronous file I/O (implemented via worker threads).
      • Signal handling.
    • Utilities: Asynchronous versions of functools and itertools modules.
  9. Create bidirectional Stapled streams

    master

    A stapled stream combines a compatible receive stream and send stream into a single bidirectional stream object.

    There are two variants:

    • StapledByteStream: Combines a ByteReceiveStream and a ByteSendStream.
    • StapledObjectStream: Combines an ObjectReceiveStream and a compatible ObjectSendStream.
  10. How AnyIO prevents lost results during cancellation

    master

    In asyncio, if a task is scheduled to resume with a value (the await is about to yield a result) and is cancelled at that exact moment, the CancelledError is raised instead of yielding the result, causing the data to be lost.

    AnyIO's cancel scopes ensure that a task which is scheduled to resume will be able to process the result of the await before the cancellation is applied.

    import asyncio
    import anyio
    
    async def receive(f):
        print(await f)
        await asyncio.sleep(1)
        print("The task will be cancelled before this is printed")
    
    async def main():
        f = asyncio.get_running_loop().create_future()
        async with anyio.create_task_group() as tg:
            tg.start_soon(receive, f)
            await asyncio.sleep(0)  # make sure the task has started
            f.set_result("hello")
            tg.cancel_scope.cancel()
    
    # Output: "hello"
    asyncio.run(main())
  11. Compare AnyIO TaskGroups with asyncio.TaskGroup

    master

    While asyncio.TaskGroup (introduced in Python 3.11) provides basic structured concurrency, it has several limitations compared to AnyIO's task groups:

    • Task Control: asyncio.TaskGroup does not provide a way to cancel or list all contained tasks, forcing developers to manually track tasks. AnyIO task groups include a cancel scope that can cancel all child tasks regardless of where they were launched.
    • Task Readiness: asyncio.TaskGroup lacks a built-in mechanism to wait until a newly launched task signals it is ready. AnyIO provides patterns for waiting until a child task is initialized and ready to proceed.
    • Cancellation Safety: In AnyIO, if a task group's cancel scope is cancelled, any tasks launched from that group after the cancellation are also automatically subject to cancellation. This prevents tasks from accidentally hanging the task group and preventing it from exiting.