A simple and flexible dependency injection framework for Python projects. This library allows you to define services, manage their lifecycle, and inject dependencies into your applications in a clean and structured way.
You can install this library using pip:
pip install simple-dependency-injector
First, load your service definitions from a YAML file or Python module and compile them:
from simple_dependency_injector import DependencyInjector
# Create an injector and load the service definitions
injector = DependencyInjector(base_path='config_path')
injector.load('services.yaml')
injector.compile()
# Access a service
service = injector.get('my_service')
You can define services in a YAML file:
services:
my_service:
class: 'tests/services/my_module.py#MyService'
arguments:
- '@another_service'
scope: singleton
another_service:
class: 'tests/services/another_module.py#AnotherService'
scope: transient
-
class
: The fully qualified class name to instantiate. -
arguments
: The list of dependencies to inject.- Service Reference (
@service_name
): This resolves to another service defined in the injector. - Tagged Services (
!tagged:tag_name
): This resolves to a list of services that have the specified tag. - Context Reference (
!context
): This resolves to the current DI context.
- Service Reference (
-
scope
: The lifecycle of the service. Options aresingleton
,transient
,request
, orambivalent
.
services:
my_service:
class: 'tests/services/my_service.py#MyService'
arguments:
- '@another_service'
- '!tagged:logging'
- '!context'
scope: singleton
When my_service
is instantiated, the @another_service
argument will resolve to the instance of another_service
, !tagged:logging
will resolve to a list of services tagged with logging
, and !context
will resolve to the current DI context.
You can assign tags to services and resolve them by tag:
services:
tagged_service:
class: 'tests/services/tagged_service.py#TaggedService'
tags:
- 'my_tag'
scope: singleton
You can then retrieve all services that have a specific tag:
services_with_tag = injector.get_list_with_tag('my_tag')
You can link services during runtime. For example, you can retrieve one service and assign its instance to another service:
injector.link('source_service', 'target_service')
This is useful when you need to dynamically replace or redirect service instances.
To use the dependency injector in Django, you can inject services into requests by creating a custom middleware.
Create a middleware to add a new dependency injection context for each request:
# middlewares.py
from simple_dependency_injector import DependencyInjector
injector = DependencyInjector(base_path='config_path')
injector.load('services.yaml')
injector.compile()
class DependencyInjectorMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.container = injector.create_context()
response = self.get_response(request)
return response
You can also create a middleware to inject services like a logger into the request context:
# middlewares.py
from .logger_service import DjangoLoggerService
class LoggerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.container.set("logger_service", DjangoLoggerService(request))
response = self.get_response(request)
return response
Add the middlewares to your MIDDLEWARE
setting in settings.py
:
# settings.py
MIDDLEWARE = [
# Other middlewares
'myapp.middlewares.DependencyInjectorMiddleware',
'myapp.middlewares.LoggerMiddleware',
]
Now you can access the services from the request container in your Django views:
# views.py
from django.http import JsonResponse
def my_view(request):
# Get the logger service from the DI container
logger = request.container.get('logger_service')
# Use the logger
logger.log('This is a log message')
return JsonResponse({'status': 'success'})
You can define services using factory methods. This is useful when the service creation logic is more complex:
services:
factory_service:
factory:
class: 'tests/services/my_module.py#MyFactory'
method: create_service
arguments:
- 'config_value'
scope: singleton
For services that need to maintain a request-specific state, you can create a new context per request and resolve services within that context:
# Create a context for each request
context = injector.create_context()
# Access services within that context
service = context.get('my_service')
To set up a local development environment for this project, follow these steps:
- Fork this repository and clone it:
git clone https://github.com/yourusername/python-simple-dependency-injector.git
cd simple-dependency-injector
- Create a virtual environment:
python -m venv venv
- Activate the virtual environment:
source venv/bin/activate
- Install the development dependencies:
pip install -r requirements-dev.txt
Now you are ready to start working on the project. You can run tests, add new features, or fix bugs.
black simple_dependency_injector tests && pylint simple_dependency_injector tests
This project includes a suite of unit tests to ensure that all functionality works as expected. The tests are located in the tests
directory, and you can run them using:
python -m unittest discover tests
This project is licensed under the MIT License. See the LICENSE
file for details.