pythondi is a lightweight dependency injection library for python
Support both sync and async functions
pip3 install pythondi
First, you have to binding classes to provider.
from pythondi import Provider
provider = Provider()
provider.bind(Repo, SQLRepo)
provider.bind(Usecase, CreateUsecase)
After binding, you need to configure it to container
from pythondi import configure, configure_after_clear
# Inject with configure
configure(provider=provider)
# Or if you want to fresh inject, use `configure_after_clear`
configure_after_clear(provider=provider)
Import inject
from pythondi import inject
Add type annotations that you want to inject dependencies
class Usecase:
def __init__(self, repo: Repo):
self.repo = repo
Add decorator
class Usecase:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
Initialize class with no arguments
usecase = Usecase()
Or, you can also inject manually through decorator arguments
class Usecase:
@inject(repo=SQLRepo)
def __init__(self, repo):
self.repo = repo
In this case, do not have to configure providers and type annotation.
from pythondi import Provider
provider = Provider()
provider.bind(Repo, SQLRepo, lazy=True)
You can use lazy initializing through lazy
option. (default False
)
For singleton, use lazy=False
.
class Usecase:
@inject(repo=SQLRepo)
def __init__(self, repo):
self.repo = repo
By default, manual injection is lazy. If you want a singleton, instantiate it like repo=SQLRepo()
.
In case of test codes, you probably want to use mock objects.
In that case, you must use keyword arguments.
class MockRepo:
pass
@inject()
def test(repo: Repo):
return repo
Yes:
test(repo=MockRepo())
No:
test(MockRepo())
test(MockRepo)
At the moment of inject, class is automatically initialized.
So you don't have to initialize your class inside of code.
Yes:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
No:
@inject()
def __init__(self, repo: Repo):
self.repo = repo()
import abc
from pythondi import Provider, configure, configure_after_clear, inject
class Repo:
"""Interface class"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self):
pass
class SQLRepo(Repo):
"""Impl class"""
def get(self):
print('SQLRepo')
class Usecase:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
if __name__ == '__main__':
# Init provider
provider = Provider()
# Bind `Impl` class to `Interface` class
provider.bind(Repo, SQLRepo)
# Inject with configure
configure(provider=provider)
# Or if you want to fresh injection, use `configure_after_clear`
configure_after_clear(provider=provider)
# Init class without arguments
u = Usecase()
from fastapi import FastAPI, APIRouter
from pythondi import Provider, configure, inject
import abc
router = APIRouter()
class Repo:
"""Interface class"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self):
pass
class SQLRepo(Repo):
"""Impl class"""
def __init__(self):
pass
def get(self):
print('SQLRepo')
@router.route('/')
def home():
usecase = Usecase()
usecase.repo.get()
return {'hello': 'world'}
class Usecase:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
def create_app():
provider = Provider()
provider.bind(Repo, SQLRepo)
configure(provider=provider)
app = FastAPI()
app.include_router(router)
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
from flask import Flask, Blueprint, jsonify
from pythondi import Provider, configure, inject
import abc
bp = Blueprint('home', __name__)
class Repo:
"""Interface class"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self):
pass
class SQLRepo(Repo):
"""Impl class"""
def __init__(self):
pass
def get(self):
print('SQLRepo')
@bp.route('/')
def home():
usecase = Usecase()
usecase.repo.get()
return jsonify({'hello': 'world'})
class Usecase:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
def create_app():
provider = Provider()
provider.bind(Repo, SQLRepo)
configure(provider=provider)
app = Flask(__name__)
app.register_blueprint(bp)
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
import abc
from sanic import Sanic, Blueprint
from sanic.response import json
from pythondi import Provider, configure, inject
class Repo:
"""Interface class"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self):
pass
class SQLRepo(Repo):
"""Impl class"""
def __init__(self):
pass
def get(self):
print('SQLRepo')
bp = Blueprint('home', url_prefix='/')
@bp.route('/')
async def home(request):
usecase = Usecase()
usecase.repo.get()
return json({'hello': 'world'})
class Usecase:
@inject()
def __init__(self, repo: Repo):
self.repo = repo
def create_app():
provider = Provider()
provider.bind(Repo, SQLRepo)
configure(provider=provider)
app = Sanic(__name__)
app.blueprint(bp)
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
"""
In case of django, just put the initializing code inside of django startup
You can use project folder's __init__.py or urls.py
"""