Skip to main content

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


Download files

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

Source Distribution

enterprython-0.5.3.tar.gz (10.4 kB view details)

Uploaded Source

Built Distribution

enterprython-0.5.3-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

File details

Details for the file enterprython-0.5.3.tar.gz.

File metadata

  • Download URL: enterprython-0.5.3.tar.gz
  • Upload date:
  • Size: 10.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for enterprython-0.5.3.tar.gz
Algorithm Hash digest
SHA256 bcbe3def95151f2b4bd4718c0d9333488e5bef1d2557712467eaa2de1c4ce6c3
MD5 79267385e04955cf54d3a75b8aff4ba0
BLAKE2b-256 1173413492a6d6cd2ae12eff264659b2466f38d4f1295f3fb127d51aad3f2df0

See more details on using hashes here.

File details

Details for the file enterprython-0.5.3-py3-none-any.whl.

File metadata

  • Download URL: enterprython-0.5.3-py3-none-any.whl
  • Upload date:
  • Size: 9.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for enterprython-0.5.3-py3-none-any.whl
Algorithm Hash digest
SHA256 29c33c3c0ea1dead72d5de4eaae224a7dcc8bb31f1d7be3b20dd61e1e9f039b4
MD5 55266a411fb33f45d2541daaba9ad4ed
BLAKE2b-256 6a6ae79237c8434c0330eea6cc990b348d5dea20519ac5d60927566708cedec7

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