Skip to main content

Decorator type based dependency injection

Project description

quickd

Decorator type-based dependency injection for Python

GitHub Workflow Status GitHub release (latest by date) GitHub PyPI - Downloads

📦 Installation

The package quickd supports Python >= 3.5. You can install it by doing:

$ pip install quickd

📜 Example

Here is a quick example:

from quickd import inject, factory


class Database:
    pass


class PostgreSQL(Database):
    def __str__(self):
        return 'PostgreSQL'


class MySQL(Database):
    def __str__(self):
        return 'MySQL'


@inject
def print_database(database: Database):
    return print(database)


@factory
def choose_database() -> Database:
    return PostgreSQL()


print_database()  # Prints: PostgreSQL
print_database(MySQL())  # Prints: MySQL

🚀 Usage

There are only 3 decorators that compose the whole framework

@factory

  • Registers an instance for a specific type for later use with @inject
  • Is mandatory to annotate the function with the return type of the class that you want to inject later
  • It is not dynamic, so the implementation can only be chosen once
from quickd import factory


@factory
def choose_database() -> Database:
    return PostgreSQL()

@inject

  • Injects dependencies to a function by matching its arguments types with what has been registered
  • As you can see below, it also works with constructors
from quickd import inject


@inject
def print_database(database: Database):
    return print(database)


class UserService:
    @inject
    def __init__(self, database: Database): pass

@service

  • Registers a class to be later injectable without using @factory
  • It also applies @inject to its constructor
from quickd import service, inject


@service
class UserService:
    def __init__(self):
        self.users = ['Bob', 'Tom']

    def all(self):
        return self.users

    def add(self, user):
        self.users.append(user)


@inject
def get_users(service: UserService):
    return service.all()


@inject
def add_user(service: UserService):
    return service.add("Pol")


get_users()  # ['Bob', 'Tom']
add_user()
get_users()  # ['Bob', 'Tom', 'Pol']

👨‍🍳 Recipes

Here are some common solutions to scenarios you will face.

Interfaces

from abc import abstractmethod
from quickd import inject, factory


class UserRepository:
    @abstractmethod
    def save(self, user):
        pass

    @abstractmethod
    def search(self, id):
        pass


class UserCreator:
    @inject
    def __int__(self, repository: UserRepository):
        self.repository = repository

    def create(self, user):
        self.repository.save(user)


class MySQLUserRepository(UserRepository):
    def __int__(self, host, password):
        self.sql = MySQLConnection(host, password)

    def save(self, user):
        self.sql.execute('INSERT ...')

    def search(self, id):
        self.sql.execute('SELECT ...')


@factory
def choose_user_repository() -> UserRepository:  # Notice super class is being used
    return MySQLUserRepository('user', '123')

Testing

Following the above example we can create a unit test mocking the persistance, which will make our tests easier and faster.

fake_user = {'id': 1, 'name': 'Tom'}


class FakeUserRepository(UserRepository):
    def save(self, user):
        assert user == fake_user


repository = FakeUserRepository()
user_creator = UserCreator(repository)

user_creator.create(fake_user)

Configuration

There are multiple ways to configure your classes. A simple approach is to use environment variables on your factory annotated methods.

import os
from quickd import factory


@factory
def choose_database() -> Database:
    username = os.environ.get("POSTGRES_USER")
    password = os.environ.get("POSTGRES_PASS")
    return PostgreSQL(username, password)

🧠 Motivation

Dependency injection provides a great way to decouple your classes in order to improve testability and maintainability.

Frameworks like Spring or Symfony are loved by the community.

I will just add a parameter to the constructor and Spring will fill with a global instance of the class

These frameworks rely heavy on the type system, to know which class should go where.

From Python 3.5 we have the typing package. This addition allows us to have the dependency injection framework that Python deserves.

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

quickd-0.1.1.tar.gz (5.7 kB view details)

Uploaded Source

Built Distribution

quickd-0.1.1-py3-none-any.whl (5.6 kB view details)

Uploaded Python 3

File details

Details for the file quickd-0.1.1.tar.gz.

File metadata

  • Download URL: quickd-0.1.1.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.9.2

File hashes

Hashes for quickd-0.1.1.tar.gz
Algorithm Hash digest
SHA256 48a19f55eca9c56c74ef1e0b199ecfc1498ba358df139bd282b45d4675308728
MD5 8568cd6653cba78c9275a3d73d167d98
BLAKE2b-256 1d140109de9a82d95bf18be7d65d9769aa677e166aa3598e6dade740478e69bc

See more details on using hashes here.

File details

Details for the file quickd-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: quickd-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 5.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.9.2

File hashes

Hashes for quickd-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9e61a6d5b6ef38b34479835e434de8efc84ed5a799c27a0cba7029fc43091509
MD5 f422a04759b49c3aa2949aa9bb496fd0
BLAKE2b-256 d35cc2de61a26053bc8d77327b0ba6f936ebd8ef313fb347568f067642031f79

See more details on using hashes here.

Supported by

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