On this page
DependencyResolver for per-request DI: sync/async factories, generator factories with reverse-order cleanup, caching, and test overrides.
#src.fastware.di
#src.fastware.di
Dependency injection container providing per-request resolution with automatic caching, generator cleanup, and scope-aware dependency override support.
Supports sync/async factory callables and sync/async generator factories (yield pattern). Generator factories get cleanup after the handler returns. Results are cached per-request: the same factory called twice returns the same resolved instance.
#_request_call_mode
def _request_call_mode(factory: Callable) -> str | NoneDetermine how factory should receive the request, by introspection.
Returns:
- -
"positional"if the factory has a parameter that can accept the
request positionally (positional-only, positional-or-keyword, or *args). The request is passed as factory(request).
- -
"keyword"if the factory has a keyword-only parameter literally
named request. The request is passed as factory(request=request).
- -
Noneif the factory takes no request slot (e.g.def f()or
def f(*, x=1)). The factory is called with no arguments.
- This prevents wrongly calling
factory(request)on a factory whose only parameters are keyword-only (which raisesTypeError).
#DependencyResolver
Resolves a dict of {name: factory} into {name: value} per request.
overrides maps an original factory to a replacement factory. When an override exists for a factory, the replacement is called instead.
#resolve
async def resolve(self, deps: dict[str, Callable], request: Any) -> tuple[dict[str, Any], list[tuple[str, Any]]]Resolve deps against request.
Returns (resolved, cleanups) where resolved is a {name: value} dict and cleanups is a list of ("sync" | "async", generator) pairs to pass to :meth:cleanup.
If a factory raises during resolution, any generator cleanups accumulated so far are run before the exception propagates.
#cleanup
async def cleanup(cleanups: list[tuple[str, Any]]) -> NoneRun generator cleanups in reverse order.
Each generator dependency must yield exactly once. If a generator yields a second time during cleanup (a multi-yield generator, like a FastAPI-style dependency written with two yield statements), that is a programming error: :class:RuntimeError is raised after all cleanups have run.
Errors raised inside a generator's cleanup code (e.g. in a finally block) are logged at ERROR level and suppressed -- a failing cleanup must not mask the handler's response or exception. They are never swallowed silently.