Context Manager Utilities¶
The contextx
module provides utilities for working with Python context managers, including seamless adaptation between synchronous and asynchronous contexts, and type-safe detection of async context managers. This is useful for writing generic code that works with both sync and async resources.
Why?¶
Python's context managers (with
and async with
) are powerful for resource management, but sometimes you need to:
- Use a synchronous context manager in an async context.
- Detect if a context manager is async or sync at runtime.
- Write generic code that works with both.
The contextx
module solves these problems with minimal boilerplate.
Features¶
- Async wrapper for sync context managers (
AsyncContextWrapper
) - Type-safe detection of async context managers (
is_async_context
) - Compatible with standard and custom context managers
Usage¶
Wrapping a Sync Context Manager for Async Use¶
Suppose you have a synchronous context manager:
from contextlib import contextmanager
@contextmanager
def my_resource():
print("enter")
yield "resource"
print("exit")
You can use it in an async context with AsyncContextWrapper
:
from escudeiro.misc.contextx import AsyncContextWrapper
async def main():
async with AsyncContextWrapper(my_resource()) as res:
print(res) # Prints "resource"
# Output:
# enter
# resource
# exit
Detecting Async Context Managers¶
You can check if a context manager is asynchronous:
from escudeiro.misc.contextx import is_async_context
from contextlib import nullcontext, asynccontextmanager
print(is_async_context(nullcontext())) # False
@asynccontextmanager
async def acontext():
yield
print(is_async_context(acontext())) # True
API Reference¶
AsyncContextWrapper
¶
@dataclass
class AsyncContextWrapper[T](AbstractAsyncContextManager):
context: AbstractContextManager[T]
def __enter__(self) -> T: ...
def __exit__(self, exc_type, exc_value, traceback) -> Any: ...
async def __aenter__(self) -> T: ...
async def __aexit__(self, exc_type, exc_value, traceback) -> Any: ...
- Description: Wraps a synchronous context manager so it can be used with
async with
. - Parameters:
context
: The synchronous context manager to wrap.
is_async_context
¶
def is_async_context[T](
context: AbstractContextManager[T] | AbstractAsyncContextManager[T],
) -> TypeIs[AbstractAsyncContextManager[T]]:
...
- Description: Returns
True
if the context manager is asynchronous (has__aenter__
and__aexit__
). - Parameters:
context
: The context manager to check.
Notes¶
AsyncContextWrapper
does not make the underlying resource truly asynchronous; it only adapts the interface.- Use
is_async_context
for runtime checks when writing generic context manager utilities.