Skip to main content

The Botox is a lightweight dependency injection pattern implementation for Python.

Project description

Botox

Botox Injector is a lightweight dependency injection implementation based on Python typing. It helps deliver the configured functional objects, decreasing coupling between a class and its dependencies.

Delivery

Global variables? Proxy objects? Application context?

There should be one (and preferably only one) obvious way to do it.

Botox helps you isolate a class from the impact of different design changes and defects. Meaning that, instead of thinking about interdependence between application modules you will now find yourself spending your time having to focus a class on the task it is designed for.

Configuration

Monkey-patching? Decorators?

Explicit is better than implicit.

Botox allows a class the flexibility of being configurable. The class rely on dependencies interface that he expect. Explicit configurations can be written separately for different situations that require different implementations of dependencies.

Usage

Now is better than never.

Botox doesn’t require any change in code behavior it can be applied to legacy code as a refactoring.

Installation

Install and update using pip:

pip install -U botox-di

Examples

Class Injection

Can be used to reduce boilerplate code in the application classes since all work to initialize or set up dependencies is handled separately.

from botox import Injector

class PaymentService:
    ...

class BillingService:
    ...

class SalesService:

    def __init__(self, payment_service: PaymentService, billing_service: BillingService):
        self.payment_service = payment_service
        self.billing_service = billing_service

injector = Injector()
injector.prepare(PaymentService)
injector.prepare(BillingService)
injector.prepare(SalesService)

sales = injector.deliver(SalesService)

assert isinstance(sales.payment_service, PaymentService)
assert isinstance(sales.billing_service, BillingService)

The result is class that is easier to unit test in isolation using stubs or mock objects that simulate other objects.

injector.prepare(PaymentService, PaymentServiceStub)

Value Injection

Can be used when exactly one object is needed to coordinate actions across the system.

from botox import Injector

class AppSettings:
    ...

settings = AppSettings()

injector = Injector()
injector.prepare(AppSettings, settings)

assert injector.deliver(AppSettings) is settings

Lambda Injection

Can be used to wrap Proxy objects in legacy code as refactoring.

from botox import Injector
from flask import g
from sqlalchemy.orm import Session

injector = Injector()
injector.prepare(Session, lambda: g.session)

Function Injection

Can be used to make factory functions with dependencies.

from botox import Injector

def create_api_client(settings: Settings):
    return ApiClient(settings.base_url, settings.key)

injector = Injector()
injector.prepare(Settings)
injector.prepare(ApiClient, create_api_client)

Celery

You can define a different application base task class to deliver dependencies into a task call.

from celery import Celery, Task
from botox import Injector

class Calculator:
    def add(self, x, y):
        return x + y

class AppTask(Task):
    def __call__(self, *args, **kwargs):
        run = self.app.injector.inject(self.run, skip=len(args))
        return run(*args, **kwargs)

app = Celery('tasks', broker='pyamqp://guest@localhost//', task_cls=AppTask)
app.injector = Injector()
app.injector.prepare(Calculator)

@app.task
def add(x, y, calculator: Calculator):
    return calculator.add(x, y)

aiohttp

You can use a middleware to deliver dependencies into a request handler.

from aiohttp import web
from botox import Injector

class HelloService:
    def get_hello_message(self, name):
        return f'Hello, {name}!'

async def handle(request, service: HelloService):
    name = request.match_info.get('name', 'Anonymous')
    text = service.get_hello_message(name)
    return web.Response(text=text)

@web.middleware
async def dependency_injection(request, handler):
    handler = request.app.injector.inject(handler, skip=1)
    return await handler(request)

app = web.Application(middlewares=[dependency_injection])
app.injector = Injector()
app.injector.prepare(HelloService)
app.add_routes([
    web.get('/', handle),
    web.get('/{name}', handle)
])

web.run_app(app)

Coroutine injection

Coroutine function also can be provided with dependencies.

def TimeClient:
    async def now():
        return await self.get_current_time()

async def handle(request, service: TimeService):
    return await service.now()

...    
async def process_request(request, injector: Injector):
    handle = injector.inject(handle)
    return await handle()
...

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

botox-di-1.4.4.tar.gz (5.3 kB view details)

Uploaded Source

Built Distribution

botox_di-1.4.4-py3-none-any.whl (6.0 kB view details)

Uploaded Python 3

File details

Details for the file botox-di-1.4.4.tar.gz.

File metadata

  • Download URL: botox-di-1.4.4.tar.gz
  • Upload date:
  • Size: 5.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for botox-di-1.4.4.tar.gz
Algorithm Hash digest
SHA256 e96ecaa55dfc3eae64a1542129fe14e0c12beeb29571035971834a039ab6d92b
MD5 5d0da4b6ee2a06382bdca17f52c5f2e5
BLAKE2b-256 0132d83aa0f6e60324933f747abd0a4ea062a7303d38b41e2afc8cfb6a4191ba

See more details on using hashes here.

File details

Details for the file botox_di-1.4.4-py3-none-any.whl.

File metadata

  • Download URL: botox_di-1.4.4-py3-none-any.whl
  • Upload date:
  • Size: 6.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.2 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for botox_di-1.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9e8ec2ecf6272fbf1c94ed50993abfb1aaab7867d2d3445b9164c9538bfb263d
MD5 d3c4a4c803957dd4f3719f937cdbfc73
BLAKE2b-256 543336c61475bb716e11ae214c935c0eca7bbf7d8f77778b5b538386b870b1a1

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