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.
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)
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.