Type-based dependency injection
Project description
.. figure:: https://github.com/Dobiasd/enterprython/raw/master/logo/enterprython.png :alt: logo
logo
|Build Status| |(License MIT 1.0)|
enterprython
Python library providing type-based dependency injection
Table of contents
Introduction <#introduction>
__Features <#features>
__Abstract base classes <#abstract-base-classes>
__Factories <#factories>
__Non-singleton services <#non-singleton-services>
__Service lists <#service-lists>
__Mixing managed and manual injection <#mixing-managed-and-manual-injection>
__Free functions as clients <#free-functions-as-clients>
__Requirements and Installation <#requirements-and-installation>
__
Introduction
If you plan to develop SOLID <https://en.wikipedia.org/wiki/SOLID>
__ /
domain-driven <https://en.wikipedia.org/wiki/Domain-driven_design>
__
(i.e., enterprisey) software, you probably
want <why_you_want_formal_dependency_injection_in_python_too.md>
__ to
apply inversion of control <https://en.wikipedia.org/wiki/Inversion_of_control>
__ in the
form of dependency injection <https://en.wikipedia.org/wiki/Dependency_injection>
__ when
writing the constructors of your classes. Also you likely want to use a
library doing the needed lookups for you based on static type
annotations, instead of manually configuring the object graph.
enterprython
provides exactly that.
.. code:: python
from enterprython import assemble, component
@component()
class Service:
def __init__(self) -> None:
self._greeting: str = 'Hello'
def greet(self, name: str) -> str:
return f'{self._greeting}, {name}!'
class Client:
def __init__(self, service: Service) -> None:
self._service = service
def run(self) -> None:
print(self._service.greet('World'))
assemble(Client).run()
Output:
.. code:: text
Hello, World!
Features
Abstract base classes
A client may depend on an abstract base class. Enterprython will inject
the matching implementation.
.. code:: python
from abc import ABC
from enterprython import assemble, component
class ServiceInterface(ABC):
...
@component()
class ServiceImpl(ServiceInterface):
...
class Client:
def __init__(self, services: ServiceInterface) -> None:
...
assemble(Client)
One singleton instance of ``ServiceImpl`` is created and injected into
``Client``.
Factories
~~~~~~~~~
Annotating a function with ``@factory()`` registers a factory for its
return type.
.. code:: python
from enterprython import assemble, component
class Service:
...
@factory()
def service_factory() -> Service:
return Service()
class Client:
def __init__(self, service: Service) -> None:
...
assemble(Client)
``service_factory`` is used to create the ``Service`` instance for
calling the constructor of ``Client``.
Non-singleton services
If a service is annotated with @component(singleton=False)
a new
instance of it is created with every injection.
.. code:: python
@component(singleton=False)
class Service:
...
class Client:
def __init__(self, service: Service) -> None:
...
Service lists
A client may depend on a list of implementations of a service interface.
.. code:: python
from abc import ABC
from typing import List
from enterprython import assemble, component
class ServiceInterface(ABC):
pass
@component()
class ServiceA(ServiceInterface):
...
@component()
class ServiceB(ServiceInterface):
...
class Client:
def __init__(self, services: List[ServiceInterface]) -> None:
...
assemble(Client)
``[ServiceA(), ServiceB()]`` is injected into ``Client``.
Mixing managed and manual injection
One part of a client's dependencies might be injected manually, the rest automatically.
.. code:: python
from enterprython import assemble, component
@component()
class ServiceA:
...
class ServiceB:
...
class Client:
def __init__(self, service_a: ServiceA, service_b: ServiceB) -> None:
...
assemble(Client, service_b=ServiceB())
service_a
comes from the DI container, service_b
from user code.
If ServiceB
also has a @component()
annotation, the manually
provided object is preferred.
Free functions as clients
Since class constructors are fundamentally just normal functions, we can
inject dependencies into free functions too.
.. code:: python
from enterprython import assemble, component
@component()
class Service:
...
def client(service: Service) -> None:
...
assemble(client)
A singleton instance of ``Service`` is created and used to call
``client``.
Requirements and Installation
-----------------------------
You need Python 3.6.5 or higher.
.. code:: bash
python3 -m pip install enterprython
Or, if you like to use latest version from this repository:
.. code:: bash
git clone https://github.com/Dobiasd/enterprython
cd enterprython
python3 -m pip install .
License
-------
Distributed under the MIT License. (See accompanying file
```LICENSE`` <https://github.com/Dobiasd/enterprython/blob/master/LICENSE>`__
or at https://opensource.org/licenses/MIT)
.. |Build Status| image:: https://travis-ci.org/Dobiasd/enterprython.svg?branch=master
:target: https://travis-ci.org/Dobiasd/enterprython
.. |(License MIT 1.0)| image:: https://img.shields.io/badge/license-MIT%201.0-blue.svg
:target: LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for enterprython-0.5.3-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 29c33c3c0ea1dead72d5de4eaae224a7dcc8bb31f1d7be3b20dd61e1e9f039b4 |
|
MD5 | 55266a411fb33f45d2541daaba9ad4ed |
|
BLAKE2b-256 | 6a6ae79237c8434c0330eea6cc990b348d5dea20519ac5d60927566708cedec7 |