← Back to archive

Repo of the Day

maldoinc/wireup

Published: Jul 4, 2025

Open repository ↗

Performant, concise, and easy-to-use dependency injection container for Python 3.8+.

Summary

Wireup is a type-driven dependency injection library for Python that validates the entire object graph when the container starts, so missing or misconfigured services surface as startup errors instead of runtime exceptions. It exposes a single @injectable decorator that handles classes, dataclasses, factories, and generator-based resources, with native integrations for FastAPI, Django, Flask, Starlette, Celery, Click, Typer, and AIOHTTP.

What it is useful for

Wireup fits applications that need the same domain code reused across web APIs, CLI commands, Celery workers, and ad-hoc scripts without rewriting wiring for each entry point. It is useful when teams want predictable lifetimes: singleton for app-wide state, scoped for per-request or per-job state, and transient for fresh instances each resolution, with scope-leakage rules checked at boot.

The library also targets patterns that other containers handle awkwardly. Fan-out work where parallel tasks need isolated state but a shared piece of context (for example, a tenant ID) is modeled with container.enter_scope({...}) rather than implicit inheritance, so workers cannot accidentally share state through the parent graph. Teams that want to keep pure domain models free of framework imports can use factories that build plain Pydantic or dataclass objects, with Wireup only touching the composition layer. For tests, container.override swaps implementations for the duration of a with block and restores the original graph automatically.

The README includes a benchmark chart comparing FastAPI throughput across DI libraries; the project itself notes that some libraries in that chart use simplified benchmark implementations, so the figure represents an upper bound for those entries rather than a direct production comparison.

How engineers can use it

The README documents the following flow.

  1. Install: pip install wireup.
  2. Decorate services with @injectable and declare dependencies as type-annotated parameters:
    @injectable
    class UserService:
        def __init__(self, db: Database) -> None:
            self.db = db
    
  3. Build a container with wireup.create_sync_container(injectables=[...]) or wireup.create_async_container(...) depending on the stack, then call the framework integration's setup(container, app). For FastAPI, annotate handlers with Injected[UserService].
  4. For values pulled from configuration, use Annotated[str, Inject(config="db_url")] and pass config={"db_url": os.environ["DB_URL"]} to the container.
  5. For resources needing teardown, write a generator factory; cleanup is dispatched by Wireup based on the chosen lifetime.
  6. In tests, wrap the call under test with with container.override({Database: in_memory_database}): to swap in fakes.

For full coverage of validation rules, lifetimes, and the free-threading (PEP 703) notes, the README points readers to the hosted docs.