Services
Wire repositories, gateways, clients, and use cases once at startup.
Zero dependencies · typed wiring · no framework lock-in
Injex keeps application wiring explicit with normal type hints, scoped lifetimes, test overrides, and graph validation before startup.
Articles: manual wiring · dependency wiring
Performance
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.
Median µs/op. Smaller is better. Synthetic graph; measure your own app.
Why it exists
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.
Wire repositories, gateways, clients, and use cases once at startup.
Create one scope per job or message while reusing long-lived clients.
Override external dependencies inside one explicit with block.
Share settings, API clients, and commands without module singletons.
Quick start
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)
Check the whole graph — missing deps, missing annotations, cycles — without constructing anything.
Singleton, transient, scoped — plus generator factories with teardown,
finalized when a scope exits or on close().
aresolve() / ascope() await async factories
and manage async-resource lifecycles; a fully-sync graph awaited this
way resolves at the sync fast-path speed.
call() wires any handler or command; pass the request or
args, the services are injected.
injex.ext.fastapi injects into routes with a per-request
scope; injex.ext.cli wires Typer/Click commands.
Mark classes with @injectable and register a whole module
with container.scan() — still explicit.
Positioning
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.
Documentation
Python dependency injection
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.