Reusable Bundles
Use reusable bundles when you need to register the same services more than once with different settings, such as multiple database clients or tenant-specific integrations.
Use a function that returns injectable factories:
from dataclasses import dataclass
from typing import Annotated
import wireup
from wireup import Inject, injectable
@dataclass
class DbClient:
dsn: str
@dataclass
class DbRepository:
client: DbClient
def make_db_bundle(*, dsn: str, qualifier: str | None = None) -> list[object]:
@injectable(qualifier=qualifier)
def db_client_factory() -> DbClient:
return DbClient(dsn=dsn)
@injectable(qualifier=qualifier)
def db_repo_factory(
client: Annotated[DbClient, Inject(qualifier=qualifier)],
) -> DbRepository:
return DbRepository(client=client)
return [db_client_factory, db_repo_factory]
Then create your container with multiple bundles:
primary = make_db_bundle(dsn="postgresql://primary-db")
analytics = make_db_bundle(
dsn="postgresql://analytics-db",
qualifier="analytics",
)
container = wireup.create_sync_container(injectables=[*primary, *analytics])
Use an unqualified default (qualifier=None) for your primary bundle, then add qualifiers only where needed:
from typing import Annotated
from wireup import Inject, Injected, injectable
@injectable
class ReportService:
def __init__(
self,
primary_repo: Injected[DbRepository],
analytics_repo: Annotated[DbRepository, Inject(qualifier="analytics")],
) -> None:
self.primary_repo = primary_repo
self.analytics_repo = analytics_repo
Why Use Bundles¶
- The setup stays in plain Python without Wireup-Specific syntax.
- It is easy to see what gets registered.
- The final graph is still validated when the container is created.
- You can combine this with Conditional Registration when different environments need different bundles.