Injector - Python Dependency Injection Framework
Injector is a Python dependency injection framework, inspired by Guice, that simplifies managing application components and their dependencies. It encourages compartmentalized code through the use of modules and supports static type checking. The library currently is at version 0.24.0 and has a regular release cadence with recent updates focusing on Python version compatibility and typing enhancements. [3, 4, 10]
Warnings
- breaking Injector 0.24.0 dropped Python 2 wheel support. The library now officially supports CPython 3.10+ and PyPy 3. Additionally, version 0.25.0 (upcoming) will drop support for Python 3.8 and 3.9. Users on older Python versions will need to upgrade. [3, 13, 20]
- breaking In `0.23.0`, the scoping behavior of `multibind()` changed. Previously, the scope applied to the entire collection (e.g., `list` or `dict` instance). Now, the scope applies to the individual bound types *within* the collection. This can lead to unexpected behavior if relying on the old scoping mechanism. [13, 20]
- deprecated Support for passing keyword arguments to `@inject` (e.g., `@inject(some_dep=SomeType)`) was deprecated in 0.11+ and subsequently removed. Similarly, injecting into non-constructor methods was also removed. `@inject` should now be used on class constructors with type hints. [12, 13]
- gotcha Injector instances maintain no global state. Attempting to directly instantiate a class with injected dependencies (e.g., `MyClass()`) will fail if the class expects dependencies to be provided by an `Injector`. You must explicitly obtain instances via `injector.get()` or `injector.create_object()`. [3, 10, 19]
- gotcha Avoid performing I/O or other long-running, blocking operations within `Module.configure` methods or `@provider` functions. The `Injector`'s internal state is protected by a lock, meaning such operations can block other threads attempting to resolve dependencies, potentially leading to performance issues or deadlocks. [12]
- gotcha There are two popular dependency injection libraries in Python with similar names: `injector` and `dependency-injector`. They are distinct libraries with different APIs and approaches. Ensure you are using the correct library's documentation and patterns for `injector` to avoid confusion. [11, 14, 15]
Install
-
pip install injector
Imports
- Injector
from injector import Injector
- inject
from injector import inject
- Module
from injector import Module
- provider
from injector import provider
- singleton
from injector import singleton
- Key
from injector import Key
Quickstart
from injector import Injector, inject, singleton, Module, provider
class Config:
def __init__(self, value: str = "default"):
self.value = value
@singleton
class DatabaseConnection:
def __init__(self, config: Config):
self.config = config
# Simulate a connection based on config
self.status = f"Connected to DB with {self.config.value}"
class Service:
@inject
def __init__(self, db: DatabaseConnection):
self.db = db
class MyModule(Module):
@singleton
@provider
def provide_config(self) -> Config:
# In a real app, this might come from env vars or a file
env_val = os.environ.get('APP_CONFIG_VALUE', 'configured_via_module')
return Config(value=env_val)
def main():
injector = Injector([MyModule])
service = injector.get(Service)
print(service.db.status)
if __name__ == "__main__":
import os
# os.environ['APP_CONFIG_VALUE'] = 'production_setting' # Uncomment to test env var injection
main()