Skip to main content

Transparent dependency injection.

Project description

https://travis-ci.org/Finistere/antidote.svg?branch=master https://codecov.io/gh/Finistere/antidote/branch/master/graph/badge.svg Documentation Status

Antidote

Antidote is dependency injection module for Python 2.7 and 3.4+. It is designed to work with simple decorators and annotations. The goal is to recognize dependencies and inject them automatically.

Features Highlight

  • Dependencies bound through type annotations and optionally from variable names and/or mapping.
  • Simple decorators to handle pretty much everything.
  • Standard dependency injection features: singleton, factories, auto-wiring (automatically injecting dependencies of defined services, etc.)
  • Python 2.7 support (without annotations, obviously :))
  • Integration with the attrs package (>= v17.1).
  • Other dependencies, such as configuration parameters, can be easily added for injection as a dictionary.

Quick Start

A simple example with a external database for which you have an adapter which will be injected in other services.

For Python 3.4+, the dependency management is straight-forward:

from antidote import antidote

class Database(object):
    """
    Class from an external library.
    """
    def __init__(self, *args, **kwargs):
        """ Initializes the database. """

# Simple way to add some configuration.
# Any object implementing __getitem__ works
antidote.container.extend(dict(
    database_host='host',
    database_user='user',
    database_password='password',
))

# Declare a factory which should be called to instantiate Database
# Variables names are used here for injection.
@antidote.factory(use_arg_name=True)
def database_factory(database_host, database_user, database_password) -> Database:
    """
    Configure your database.
    """
    return Database(
        host=database_host,
        user=database_user,
        password=database_password
    )

# Declare DatabaseWrapper as a dependency to be injected
@antidote.register
class DatabaseWrapper(object):
    """
    Your class to manage the database.
    """

    # Dependencies of __init__() are injected by default when registering
    # a dependency.
    def __init__(self, db: Database):
        self.db = db


@antidote.inject
def f(db: DatabaseWrapper):
    """ Do something with your database. """

For Python 2, the example is a bit more verbose as you need to compensate for the lack of annotations:

from antidote import antidote


class Database(object):
    """
    Class from an external library.
    """
    def __init__(self, *args, **kwargs):
        """ Initializes the database. """

# Simple way to add some configuration.
# Any object implementing __getitem__ works
antidote.container.extend(dict(
    database_host='host',
    database_user='user',
    database_password='password',
))

# Declare a factory which should be called to instantiate Database
# Variables names are used here for injection.
# PY2: The id of the returned service is specified
@antidote.factory(use_arg_name=True, id=Database)
def database_factory(database_host, database_user, database_password):
    """
    Configure your database.
    """
    return Database(
        host=database_host,
        user=database_user,
        password=database_password
    )

# Declare DatabaseWrapper as a dependency to be injected
# PY2: A class-wide argument -> dependency mapping is specified,
@antidote.register(mapping=dict(db=Database))
class DatabaseWrapper(object):
    """
    Your class to manage the database.
    """

    # Dependencies of __init__() are injected by default when registering
    # a dependency.
    def __init__(self, db):
        self.db = db

# PY2: An argument -> dependency mapping is specified
@antidote.inject(mapping=dict(db=DatabaseWrapper))
def f(db):
    """ Do something with your database. """

Documentation

The documentation is available at https://antidote.readthedocs.io/.

Why ?

Dependency injection is, IMHO, a fundamental tool when working on projects. As it grows the more necessary it becomes to decouple your code by defining clearly in only one place how an object or a function should be called with which dependencies.

So while searching for a dependency injection library, I had three requirements in mind:

  • Use of annotations compatible with type checker such as mypy to inject dependencies. But other ways should exist, as configuration parameters cannot be injected this way for example.
  • IMHO, the strict minimum of a dependency injection library: services, factories, and something to inject those in any callable which injects their dependencies.
  • The library should be easy to integrate in existing code, be it in Python 2 (it’s not gone, yet) or 3. Ideally one should be able to use injected classes or functions like any other. Usage should be transparent, which leads to easier integration and adoption.

However, I did not found a suitable library and was actually surprised to see that dependency injection was not commonly used in Python. So I created this project to answer those requirements.

TODO

  • Better support for configuration (ConfigParser typically)
  • tags to filter services and retrieve a list of them.
  • proxies ?

License

MIT

Project details


Download files

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

Files for antidote, version 0.1.0
Filename, size File type Python version Upload date Hashes
Filename, size antidote-0.1.0-py2.py3-none-any.whl (16.0 kB) File type Wheel Python version py2.py3 Upload date Hashes View hashes
Filename, size antidote-0.1.0.tar.gz (10.6 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page