container_context

Sometimes you can’t (or don’t want to) pass a diwire.Container through every layer of your app. For those cases, diwire provides diwire.container_context: a context-local global container built on contextvars (with a thread-local fallback for threadpool execution).

The idea

  • Configure the container at startup.

  • Call container_context.set_current(container).

  • Use decorators to resolve injected callables later, without importing the container.

Basic usage

from dataclasses import dataclass
from typing import Annotated

from diwire import Container, Injected, Lifetime, container_context


@container_context.register(lifetime=Lifetime.SINGLETON)
@dataclass
class Service:
    name: str = "diwire"


@container_context.resolve()
def greet(service: Annotated[Service, Injected()]) -> str:
    return f"hello {service.name}"


container = Container()
container_context.set_current(container)

print(greet())  # => hello diwire

Scoped usage

If you pass scope=... to container_context.resolve(), the wrapper creates a scope per call (same idea as container.resolve(func, scope=...)).

Framework integration

container_context is especially useful in web frameworks where:

  • handlers might run in different tasks/threads

  • you want handler signatures to stay “framework-friendly”

See: FastAPI