Skip to main content

Python dependency injection library

Project description

pythondi

license pypi pyversions badge Downloads


pythondi is a lightweight dependency injection library for python

Support both sync and async functions

Installation

pip3 install pythondi

Usage

First, you have to binding classes to provider.

There is three different ways to binding.

  • Binding one by one
from pythondi import Provider


provider = Provider()
provider.bind(Repo, SQLRepo)
provider.bind(Usecase, CreateUsecase)
  • Binding at initialization
from pythondi import Provider


provider = Provider(cls=Repo, new_cls=SQLRepo)
  • Binding at initialization with dictionary
from pythondi import Provider


provider = Provider(classes={Repo: SQLRepo, 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.

For test

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)

Note

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

General example

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

FastAPI example

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)

Flask example

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)

Sanic example

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)

Django example

"""
In case of django, just put the initializing code inside of django startup

You can use project folder's __init__.py or urls.py
"""

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pythondi-1.2.1.tar.gz (6.9 kB view details)

Uploaded Source

File details

Details for the file pythondi-1.2.1.tar.gz.

File metadata

  • Download URL: pythondi-1.2.1.tar.gz
  • Upload date:
  • Size: 6.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/42.0.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.7.6

File hashes

Hashes for pythondi-1.2.1.tar.gz
Algorithm Hash digest
SHA256 be47fca479ba302e772618a118af0376739e6be9f0a5c07cd7ac7bc536c881e8
MD5 17ddeecf09bd43200d80d4157ded9995
BLAKE2b-256 ba2733a4f888465012d8cc768301e79118443dc7bbcabfd6700f30cecc01bf0c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page