Uncalled-for
Async dependency injection for Python functions. It allows declaring function dependencies as parameter defaults, which are then resolved when the function runs, offering a zero-ceremony, container-less, and configuration-free approach. The library is async-native and built on standard library features like `AsyncExitStack` and `ContextVar`. It supports context manager lifecycle for generators and nested dependencies, with caching to ensure each dependency resolves once per call. Current version is 0.2.0, with an active release cadence as seen from recent GitHub releases.
Warnings
- breaking The `__aexit__` signature for custom `Dependency` classes changed between versions 0.1.0 and 0.1.1. If you implemented custom asynchronous dependencies, ensure their `__aexit__` method matches the full expected signature.
- gotcha As of version 0.1.2, the library reports concrete types for duplicate single dependencies. This change might alter behavior or error reporting if your application previously had ambiguously defined duplicate dependencies that were implicitly resolved.
- gotcha The library explicitly requires Python 3.10 or newer. Using it with older Python versions will result in `SyntaxError` or `ImportError` due to modern language features utilized.
- gotcha As an async-native library built on `AsyncExitStack` and `ContextVar`, proper asynchronous context management is crucial. Misunderstanding `await` usage or resource lifecycle in async generators can lead to unclosed resources or unexpected behavior.
Install
-
pip install uncalled-for
Imports
- call
from uncalled_for import call
Quickstart
import asyncio
from uncalled_for import call
# Define an async generator for a mock database connection
async def get_db_connection():
print("Connecting to DB...")
try:
# Simulate a connection object
yield "mock_db_connection_obj"
finally:
print("Closing DB connection...")
# Define a function that depends on the database connection
async def get_user_data(db_connection: str):
print(f"Fetching user data using: {db_connection}")
await asyncio.sleep(0.1) # Simulate async I/O
return f"User data fetched with {db_connection}"
# Define the main application function
async def main_app_logic():
# 'call' resolves get_db_connection and injects its yielded value into get_user_data
data = await call(get_user_data, db_connection=get_db_connection)
print(f"Received: {data}")
if __name__ == "__main__":
asyncio.run(main_app_logic())