Registration¶
diwire can auto-wire many graphs with zero registrations, but real applications typically need explicit providers for:
configuration objects (instances / singletons)
interfaces / protocols (bind to a concrete implementation)
resources (sessions/clients with cleanup)
multiple implementations (named components)
Direct registration APIs¶
Add (concrete types)¶
Use diwire.Container.add() when you want to resolve provides but construct concrete_type:
from typing import Protocol
from diwire import Container
class Clock(Protocol):
def now(self) -> str: ...
class SystemClock:
def now(self) -> str:
return "now"
container = Container()
container.add(SystemClock, provides=Clock)
Instances¶
Use diwire.Container.add_instance() to bind an already-created object:
from diwire import Container
class Config: ...
container = Container()
container.add_instance(Config())
Factories¶
Use diwire.Container.add_factory() for custom construction logic (sync or async factories are supported):
from diwire import Container
class Client: ...
def build_client() -> Client:
return Client()
container = Container()
container.add_factory(build_client, provides=Client)
Cleanup providers¶
For deterministic cleanup, use:
diwire.Container.add_generator()for generator/async-generator providersdiwire.Container.add_context_manager()for (async) context manager providers
add_generator() validates registrations by default and requires every yield / yield from in the provider
body to be inside a try block with a non-empty finally. If you intentionally want to skip this validation for
a specific registration, pass require_generator_finally=False.
See Scopes & cleanup for a runnable cleanup example.
Re-registering (overrides)¶
Registrations are replaceable. Registering a provider again for the same key replaces the previous provider. This is useful for tests and environment-based swapping.
Next¶
Continue with Lifetimes and Scopes & cleanup to control caching and cleanup.