fastware v0.6.0 /src.fastware.di
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

python
def _request_call_mode(factory: Callable) -> str | None

Determine 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).

  • - None if 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 raises TypeError).

#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

python
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

python
async def cleanup(cleanups: list[tuple[str, Any]]) -> None

Run 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.

Search