Zero dependencies · typed wiring · no framework lock-in

Small typed dependency injection for Python.

Injex keeps application wiring explicit with normal type hints, scoped lifetimes, test overrides, and graph validation before startup.

Python 3.10–3.14 PEP 561 typed No runtime deps 0.333 µs/op benchmark

Articles: manual wiring · dependency wiring

0runtime dependencies
3lifetimes: singleton, transient, scoped
0.333 µsmedian resolve benchmark
typedconstructor injection from annotations

Performance

Small container. Fast hot path.

Injex caches dependency plans and uses a fast resolve path for the common service-layer graph: singleton infrastructure plus transient application services.

On this synthetic benchmark, Injex resolves the graph in 0.333 µs/op, ahead of Wireup same-scope on the same machine while staying zero-dependency and explicit.

Injex 0.333 µs/op median resolve time
manual0.266
Injex0.333
dishka0.786
Wireup same scope0.872
Wireup scope/op1.544
dependency-injector1.709

Median µs/op. Smaller is better. Synthetic graph; measure your own app.

Why it exists

Keep construction at the edge.

Small Python apps often start with direct constructor calls. That is the right default. Injex becomes useful when the same service graph is repeated across API startup, CLI commands, workers, and tests.

Keep one composition root. Keep application code framework-agnostic. Validate the graph before real services are created.

01

Services

Wire repositories, gateways, clients, and use cases once at startup.

02

Workers

Create one scope per job or message while reusing long-lived clients.

03

Tests

Override external dependencies inside one explicit with block.

04

CLIs

Share settings, API clients, and commands without module singletons.

Quick start

Normal Python types. One small container.

Register services at the boundary, validate wiring, then resolve the application service. Constructors stay plain and readable.

from injex import Container

class Repository:
    pass

class Service:
    def __init__(self, repo: Repository):
        self.repo = repo

container = Container()
container.add_singleton(Repository)
container.add_transient(Service)
container.assert_valid()

service = container.resolve(Service)

Validate before startup

Check the whole graph — missing deps, missing annotations, cycles — without constructing anything.

Lifetimes & resources

Singleton, transient, scoped — plus generator factories with teardown, finalized when a scope exits or on close().

Async, first-class

aresolve() / ascope() await async factories and manage async-resource lifecycles; a fully-sync graph awaited this way resolves at the sync fast-path speed.

Inject into functions

call() wires any handler or command; pass the request or args, the services are injected.

FastAPI & CLI

injex.ext.fastapi injects into routes with a per-request scope; injex.ext.cli wires Typer/Click commands.

Auto-registration

Mark classes with @injectable and register a whole module with container.scan() — still explicit.

Positioning

Small by design, useful by contract.

Injex is not a configuration framework. It covers the boring path: typed constructor injection, scoped lifetimes, factories, named registrations, test overrides, and validation without object creation.

Use Injex when

  • one service graph is reused by APIs, CLIs, workers, and tests;
  • test doubles should replace external services explicitly;
  • startup should validate wiring before real objects are built.

Skip it when

  • manual constructor calls are still clear enough;
  • your framework dependency system covers every entrypoint;
  • you need a large provider/configuration DSL.

Documentation

Docs built around decisions, not only API calls.

Python dependency injection

A small Python DI container for explicit service wiring.

Injex is a Python dependency injection container for projects that want typed constructor injection, clean architecture boundaries, startup graph validation, and test overrides without adding a large provider framework.

python dependency injection python di container typed dependency injection clean architecture python