sniffio: Sniff out which async library your code is running under

You’re writing a library. You’ve decided to be ambitious, and support multiple async I/O packages, like Trio, and asyncio, and … You’ve written a bunch of clever code to handle all the differences. But… how do you know which piece of clever code to run?

This is a tiny package whose only purpose is to let you detect which async library your code is running under.

This library is maintained by the Trio project, as a service to the async Python community as a whole.

Usage

sniffio.current_async_library()

Detect which async library is currently running.

The following libraries are currently supported:

Library Requires Magic string
Trio Trio v0.6+ "trio"
Curio
"curio"
asyncio   "asyncio"
Trio-asyncio v0.8.2+ "trio" or "asyncio", depending on current mode
Returns:A string like "trio".
Raises:AsyncLibraryNotFoundError – if called from synchronous context, or if the current async library was not recognized.

Examples

from sniffio import current_async_library

async def generic_sleep(seconds):
    library = current_async_library()
    if library == "trio":
        import trio
        await trio.sleep(seconds)
    elif library == "asyncio":
        import asyncio
        await asyncio.sleep(seconds)
    # ... and so on ...
    else:
        raise RuntimeError(f"Unsupported library {library!r}")
exception sniffio.AsyncLibraryNotFoundError

Adding support to a new async library

If you’d like your library to be detected by sniffio, it’s pretty easy.

Step 1: Pick the magic string that will identify your library. To avoid collisions, this should match your library’s name on PyPI.

Step 2: There’s a special contextvars.ContextVar object:

sniffio.current_async_library_cvar

Make sure that whenever your library is running, this is set to your identifier string. In most cases, this will be as simple as:

from sniffio import current_async_library_cvar

# Your library's run function
def run(...):
     token = current_async_library_cvar.set("my-library's-PyPI-name")
     try:
         # The actual body of your run() function:
         ...
     finally:
         current_async_library_cvar.reset(token)

Step 3: Send us a PR to add your library to the list of supported libraries above.

That’s it!

Notes:

On older Pythons without native contextvars support, sniffio transparently uses the official contextvars backport, so you don’t need to worry about that.

There are libraries that can switch back and forth between different async modes within a single call-task – like trio_asyncio or Twisted’s asyncio operability. These libraries should make sure to set the value back and forth at appropriate points.

The general rule of thumb: current_async_library_cvar should be set to X exactly at those moments when await X.sleep(...) will work.

Warning

You shouldn’t attempt to read the value of current_async_library_cvar directly – current_async_library() has a little bit more cleverness than that.