Python dependency injection
Why build a tiny Python DI container?
Python does not need dependency injection everywhere. Injex exists for a narrower case: service code that should stay explicit, typed, testable, and independent from a web framework.
The problem is not object creation. It is repeated wiring.
Direct constructor calls are a good default. They are readable and they keep dependencies visible. The problem starts when the same graph is recreated in API startup, CLI commands, background workers, tests, and one-off scripts.
At that point the project needs a composition root: one place where repositories, clients, gateways, settings, and use cases are connected. Injex gives that structure without turning the application into a provider-configuration project.
Why not use only framework dependencies?
Framework dependency systems are useful when all entrypoints live inside the framework. Many real projects do not look like that. The same service layer may be called by a web API, a CLI task, a queue worker, and tests. Injex keeps that graph portable.
What makes Injex small?
- No runtime dependencies.
- No large provider DSL.
- No required decorators for constructor injection.
- Normal Python type hints as the dependency contract.
- Scoped test overrides and startup validation as practical DX features.
Where it fits best
| Project shape | Why Injex helps |
|---|---|
| Service layer + CLI | One graph can be reused outside web handlers. |
| Background workers | Scoped lifetimes work well for jobs and messages. |
| Clean architecture | Use cases depend on interfaces, not framework hooks. |
| Tests with external services | override() replaces gateways with fakes in one block. |
The trade-off
A DI container is still another abstraction. If manual wiring is clear, keep manual wiring. Injex is designed for the point where explicit wiring needs a small home, not for replacing simple Python code with ceremony.
Try it when: your Python app has a shared service graph, tests need clean overrides, and startup should validate dependencies before real objects are constructed.