python-dependency-injector/README.rst

314 lines
12 KiB
ReStructuredText
Raw Normal View History

2016-03-17 03:12:34 +03:00
===========================================================
Dependency Injector - Python dependency injection framework
===========================================================
2015-04-03 00:29:00 +03:00
*Dependency Injector* is a Python dependency injection framework. It was
2016-10-06 23:15:41 +03:00
designed to be unified, developer-friendly tool that helps to implement
dependency injection pattern in formal, pretty, Pythonic way.
*Dependency Injector* framework key features are:
+ Easy, smart, pythonic style.
+ Obvious, clear structure.
+ Extensibility and flexibility.
+ Memory efficiency.
+ Thread safety.
+ Documentation.
+ Semantic versioning.
Status
------
2015-04-03 00:29:00 +03:00
2016-04-25 11:07:47 +03:00
+---------------------------------------+----------------------------------------------------------------------------------------+
| *PyPi* | .. image:: https://img.shields.io/pypi/v/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Latest Version |
| | .. image:: https://img.shields.io/pypi/l/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: License |
+---------------------------------------+----------------------------------------------------------------------------------------+
| *Python versions and implementations* | .. image:: https://img.shields.io/pypi/pyversions/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Supported Python versions |
| | .. image:: https://img.shields.io/pypi/implementation/dependency_injector.svg |
| | :target: https://pypi.python.org/pypi/dependency_injector/ |
| | :alt: Supported Python implementations |
+---------------------------------------+----------------------------------------------------------------------------------------+
| *Builds and tests coverage* | .. image:: https://travis-ci.org/ets-labs/python-dependency-injector.svg?branch=master |
| | :target: https://travis-ci.org/ets-labs/python-dependency-injector |
| | :alt: Build Status |
| | .. image:: https://coveralls.io/repos/ets-labs/python-dependency-injector/badge.svg |
| | :target: https://coveralls.io/r/ets-labs/python-dependency-injector |
| | :alt: Coverage Status |
+---------------------------------------+----------------------------------------------------------------------------------------+
2015-08-31 16:31:38 +03:00
2016-10-06 23:15:41 +03:00
Dependency Injection
--------------------
`Dependency injection`_ is a software design pattern that implements
`Inversion of control`_ for resolving dependencies. Formally, if object **A**
depends on object **B**, object **A** must not create or import object **B**,
but provide a way for injecting object **B** (object **B** could be injected
into object **A** in several ways: by passing it as ``__init__`` argument, by
setting it as attribute's value or by passing it as method's argument).
Dependency injection pattern has few strict rules that should be followed:
+ Object **A** (the client) delegates to external code (the dependency
injector) the responsibility of providing its dependencies - object **B**
(the service).
+ The client doesn't know how to create the service, it knows only interface
2016-10-07 17:32:24 +03:00
of service. The service doesn't know that it is used by the client.
+ The dependency injector knows how to create the client and the service, it
also knows that the client depends on the service, and knows how to inject
the service into the client.
+ The client and the service know nothing about the dependency injector.
Dependency injection pattern provides next advantages:
+ Control on application structure.
+ Decreased coupling between application components.
+ Increased code reusability.
+ Increased testability.
+ Increased maintainability.
+ Reconfiguration of system without rebuilding.
Next two examples demonstrate refactoring of a small piece of code to
dependency injection pattern:
.. code-block:: python
"""Car & Engine example."""
class Engine(object):
"""Example engine."""
class Car(object):
"""Example car."""
def __init__(self):
"""Initializer."""
self.engine = Engine() # Engine is a "hardcoded" dependency
if __name__ == '__main__':
car = Car() # Application creates Car's instance
``Car`` **creates** an ``Engine`` during its creation. Really? Does it make
more sense than creating an ``Engine`` separately and then
**inject it into** ``Car`` when ``Car`` is being created? Looks more
realistic, right?
.. code-block:: python
"""Refactored Car & Engine example that demonstrates dependency injection."""
class Engine(object):
"""Example engine."""
class Car(object):
"""Example car."""
def __init__(self, engine):
"""Initializer."""
self.engine = engine # Engine is an "injected" dependency
if __name__ == '__main__':
engine = Engine() # Application creates Engine's instance
car = Car(engine) # and inject it into the Car's instance
2016-10-06 23:15:41 +03:00
Example of dependency injection
-------------------------------
2015-04-03 00:35:22 +03:00
Brief example below demonstrates usage of *Dependency Injector* for creating
several IoC containers for some microservice system:
2016-05-18 23:18:29 +03:00
2016-03-14 01:04:55 +03:00
.. code-block:: python
2016-10-06 22:48:43 +03:00
"""Example of dependency injection in Python."""
2016-10-06 22:48:43 +03:00
import logging
import sqlite3
2016-10-06 22:48:43 +03:00
2016-05-18 23:18:29 +03:00
import boto.s3.connection
2016-09-18 23:00:10 +03:00
import example.main
2016-05-18 23:18:29 +03:00
import example.services
2016-06-01 19:52:10 +03:00
import dependency_injector.containers as containers
import dependency_injector.providers as providers
2016-04-20 14:25:40 +03:00
2016-05-27 14:39:25 +03:00
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
2016-10-06 22:48:43 +03:00
logger = providers.Singleton(logging.Logger, name='example')
2016-04-20 14:19:54 +03:00
database = providers.Singleton(sqlite3.connect, ':memory:')
2016-05-18 23:18:29 +03:00
s3 = providers.Singleton(boto.s3.connection.S3Connection,
2016-04-20 14:19:54 +03:00
aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
2016-05-27 14:39:25 +03:00
class Services(containers.DeclarativeContainer):
"""IoC container of business service providers."""
2016-05-18 23:18:29 +03:00
users = providers.Factory(example.services.Users,
2016-10-06 22:48:43 +03:00
logger=Platform.logger,
2016-04-20 14:19:54 +03:00
db=Platform.database)
2016-05-18 23:18:29 +03:00
auth = providers.Factory(example.services.Auth,
2016-10-06 22:48:43 +03:00
logger=Platform.logger,
2016-04-20 14:19:54 +03:00
db=Platform.database,
token_ttl=3600)
2016-06-01 19:52:10 +03:00
photos = providers.Factory(example.services.Photos,
2016-10-06 22:48:43 +03:00
logger=Platform.logger,
2016-06-01 19:52:10 +03:00
db=Platform.database,
s3=Platform.s3)
2016-05-18 23:18:29 +03:00
2016-09-18 23:00:10 +03:00
class Application(containers.DeclarativeContainer):
"""IoC container of application component providers."""
2016-05-18 23:18:29 +03:00
2016-09-18 23:00:10 +03:00
main = providers.Callable(example.main.main,
users_service=Services.users,
auth_service=Services.auth,
photos_service=Services.photos)
2016-05-18 23:18:29 +03:00
2016-10-06 23:15:41 +03:00
Next example demonstrates run of dependency injection example application
defined above:
2016-09-18 23:00:10 +03:00
.. code-block:: python
2016-10-06 22:48:43 +03:00
"""Run dependency injection example application.
Instructions for running:
python run.py 1 secret photo.jpg
"""
2016-09-18 23:00:10 +03:00
2016-10-06 22:48:43 +03:00
import sys
import logging
from containers import Platform, Application
2016-04-20 14:19:54 +03:00
if __name__ == '__main__':
2016-10-06 22:48:43 +03:00
# Configure platform logger:
Platform.logger().addHandler(logging.StreamHandler(sys.stdout))
# Run application:
Application.main(uid=sys.argv[1],
password=sys.argv[2],
photo=sys.argv[3])
2016-09-18 23:00:10 +03:00
# Previous call is an equivalent of next operations:
#
2016-10-06 22:48:43 +03:00
# logger = logging.Logger(name='example')
2016-09-18 23:00:10 +03:00
# database = sqlite3.connect(':memory:')
# s3 = boto.s3.connection.S3Connection(aws_access_key_id='KEY',
# aws_secret_access_key='SECRET')
#
2016-10-06 22:48:43 +03:00
# example.main.main(uid=sys.argv[1],
# password=sys.argv[2],
# photo=sys.argv[3],
# users_service=example.services.Users(logger=logger,
# db=database),
# auth_service=example.services.Auth(logger=logger,
# db=database,
2016-09-18 23:00:10 +03:00
# token_ttl=3600),
2016-10-06 22:48:43 +03:00
# photos_service=example.services.Photos(logger=logger,
# db=database,
2016-09-18 23:00:10 +03:00
# s3=s3))
2016-10-06 22:48:43 +03:00
#
# Output:
#
# User 1 has been found in database
# User 1 has been successfully authenticated
# Photo photo.jpg has been successfully uploaded by user 1
2016-10-06 23:15:41 +03:00
Alternative definition styles of providers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2016-05-18 23:18:29 +03:00
*Dependecy Injector* supports few other styles of dependency injections
definition.
IoC containers from previous example could look like these:
2016-05-18 23:18:29 +03:00
.. code-block:: python
2016-05-27 14:39:25 +03:00
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
2016-05-18 23:18:29 +03:00
2016-10-06 23:15:41 +03:00
logger = providers.Singleton(logging.Logger) \
.add_kwargs(name='example')
2016-05-18 23:18:29 +03:00
database = providers.Singleton(sqlite3.connect) \
2016-05-29 16:39:39 +03:00
.add_args(':memory:')
2016-05-18 23:18:29 +03:00
s3 = providers.Singleton(boto.s3.connection.S3Connection) \
2016-05-29 16:39:39 +03:00
.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
2016-05-18 23:18:29 +03:00
or like this these:
2016-05-18 23:18:29 +03:00
.. code-block:: python
2016-05-27 14:39:25 +03:00
class Platform(containers.DeclarativeContainer):
"""IoC container of platform service providers."""
2016-05-18 23:18:29 +03:00
2016-10-06 23:15:41 +03:00
logger = providers.Singleton(logging.Logger)
logger.add_kwargs(name='example')
2016-05-18 23:18:29 +03:00
database = providers.Singleton(sqlite3.connect)
2016-05-29 16:39:39 +03:00
database.add_args(':memory:')
2016-05-18 23:18:29 +03:00
s3 = providers.Singleton(boto.s3.connection.S3Connection)
2016-05-29 16:39:39 +03:00
s3.add_kwargs(aws_access_key_id='KEY',
aws_secret_access_key='SECRET')
2016-10-06 23:15:41 +03:00
2015-08-31 16:31:38 +03:00
You can get more *Dependency Injector* examples in ``/examples`` directory on
2015-04-03 00:40:03 +03:00
GitHub:
2016-04-25 11:07:47 +03:00
https://github.com/ets-labs/python-dependency-injector
2015-04-03 00:35:22 +03:00
2016-10-07 17:32:24 +03:00
Installation
------------
*Dependency Injector* library is available on `PyPi`_::
pip install dependency_injector
Documentation
-------------
*Dependency Injector* documentation is hosted on ReadTheDocs:
- `User's guide`_
- `API docs`_
2015-04-03 00:35:22 +03:00
2015-04-03 00:33:28 +03:00
Feedback
2015-04-03 00:35:22 +03:00
--------
2015-04-03 00:33:28 +03:00
Feel free to post questions, bugs, feature requests, proposals etc. on
2015-08-31 16:31:38 +03:00
*Dependency Injector* GitHub Issues:
2015-04-03 00:33:28 +03:00
2016-04-25 11:07:47 +03:00
https://github.com/ets-labs/python-dependency-injector/issues
2015-04-03 00:33:28 +03:00
Your feedback is quite important!
2015-04-03 00:29:00 +03:00
.. _Dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
.. _Inversion of control: https://en.wikipedia.org/wiki/Inversion_of_control
2015-08-31 16:31:38 +03:00
.. _PyPi: https://pypi.python.org/pypi/dependency_injector
2016-04-25 11:07:47 +03:00
.. _User's guide: http://python-dependency-injector.ets-labs.org/en/stable/
.. _API docs: http://python-dependency-injector.ets-labs.org/en/stable/api/