You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
2.8 KiB
Python

from contextvars import ContextVar
from typing import Optional
import sys
import threading
current_async_library_cvar = ContextVar(
"current_async_library_cvar", default=None
) # type: ContextVar[Optional[str]]
class _ThreadLocal(threading.local):
# Since threading.local provides no explicit mechanism is for setting
# a default for a value, a custom class with a class attribute is used
# instead.
name = None # type: Optional[str]
thread_local = _ThreadLocal()
class AsyncLibraryNotFoundError(RuntimeError):
pass
def current_async_library() -> str:
"""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:
.. code-block:: python3
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}")
"""
value = thread_local.name
if value is not None:
return value
value = current_async_library_cvar.get()
if value is not None:
return value
# Need to sniff for asyncio
if "asyncio" in sys.modules:
import asyncio
try:
current_task = asyncio.current_task # type: ignore[attr-defined]
except AttributeError:
current_task = asyncio.Task.current_task # type: ignore[attr-defined]
try:
if current_task() is not None:
return "asyncio"
except RuntimeError:
pass
# Sniff for curio (for now)
if 'curio' in sys.modules:
from curio.meta import curio_running
if curio_running():
return 'curio'
raise AsyncLibraryNotFoundError(
"unknown async library, or not in async context"
)