Skip to content

Commit

Permalink
Merge branch 'release/3.44.0' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
rmk135 committed Sep 14, 2020
2 parents f56b539 + 1f136e4 commit 53b7ad0
Show file tree
Hide file tree
Showing 14 changed files with 4,201 additions and 4,065 deletions.
151 changes: 35 additions & 116 deletions README.rst
Expand Up @@ -54,103 +54,23 @@ What is ``Dependency Injector``?

It helps implementing the dependency injection principle.

What is dependency injection?
-----------------------------

Dependency injection is a principle that helps to decrease coupling and increase cohesion.

What is coupling and cohesion?

Coupling and cohesion are about how tough the components are tied.

- **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way
to disassemble.
- **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and
assemble back or assemble a different way. It is an opposite to high coupling.

When the cohesion is high the coupling is low.

Low coupling brings a flexibility. Your code becomes easier to change and test.

How to implement dependency injection?
--------------------------------------

Objects do not create each other anymore. They provide a way to inject the dependencies instead.

Before:

.. code-block:: python
import os
class ApiClient:
def __init__(self):
self.api_key = os.getenv('API_KEY') # <-- the dependency
self.timeout = os.getenv('TIMEOUT') # <-- the dependency
class Service:
def __init__(self):
self.api_client = ApiClient() # <-- the dependency
if __name__ == '__main__':
service = Service()
After:

.. code-block:: python
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key # <-- the dependency is injected
self.timeout = timeout # <-- the dependency is injected
class Service:
def __init__(self, api_client: ApiClient):
self.api_client = api_client # <-- the dependency is injected
if __name__ == '__main__':
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
timeout from a configuration file or even get them from a database.

``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
stub or other compatible object.

Flexibility comes with a price.

Now you need to assemble the objects like this::

service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))

The assembly code might get duplicated and it'll become harder to change the application structure.

Here comes the ``Dependency Injector``.

What does the Dependency Injector do?
-------------------------------------

With the dependency injection pattern objects loose the responsibility of assembling the
dependencies. The ``Dependency Injector`` absorbs that responsibility.

``Dependency Injector`` helps to assemble the objects.

It provides a container and providers that help you with the objects assembly. When you
need an object you get it from the container. The rest of the assembly work is done by the
framework:
Key features of the ``Dependency Injector``:

- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``,
``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your
objects. See `Providers <http://python-dependency-injector.ets-labs.org/providers/index.html>`_.
- **Overriding**. Can override any provider by another provider on the fly. This helps in testing
and configuring dev / stage environment to replace API clients with stubs etc. See
`Provider overriding <http://python-dependency-injector.ets-labs.org/providers/overriding.html>`_.
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
and dictionaries.
See `Configuration provider <http://python-dependency-injector.ets-labs.org/providers/configuration.html>`_.
- **Containers**. Provides declarative and dynamic containers.
See `Containers <http://python-dependency-injector.ets-labs.org/containers/index.html>`_.
- **Performance**. Fast. Written in ``Cython``.
- **Typing**. Provides typing stubs, ``mypy``-friendly.
See `Typing and mypy <http://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.

.. code-block:: python
Expand Down Expand Up @@ -180,28 +100,18 @@ framework:
service = container.service()
Retrieving of the ``Service`` instance now is done like this::

service = container.service()

Objects assembling is consolidated in the container. When you need to make a change you do it in
one place.

When doing a testing you call the ``container.api_client.override()`` to replace the real API
client with a mock:

.. code-block:: python
from unittest import mock
With the ``Dependency Injector`` you keep **application structure in one place**.
This place is called **the container**. You use the container to manage all the components of the
application. All the component dependencies are defined explicitly. This provides the control on
the application structure. It is **easy to understand and change** it.

.. figure:: https://raw.githubusercontent.com/wiki/ets-labs/python-dependency-injector/img/di-map.svg
:target: https://github.com/ets-labs/python-dependency-injector

with container.api_client.override(mock.Mock()):
service = container.service()
You can override any provider with another provider.
*The container is like a map of your application. You always know what depends on what.*

It also helps you in configuring project for the different environments: replace an API client
with a stub on the dev or stage.
Visit the docs to know more about the
`Dependency injection and inversion of control in Python <http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html>`_.

Installation
------------
Expand All @@ -215,6 +125,15 @@ Documentation

The documentation is available on the `Read The Docs <http://python-dependency-injector.ets-labs.org/>`_

Examples
--------

Choose one of the following:

- `Application example (single container) <http://python-dependency-injector.ets-labs.org/examples/application-single-container.html>`_
- `Application example (multiple containers) <http://python-dependency-injector.ets-labs.org/examples/application-multiple-containers.html>`_
- `Decoupled packages example (multiple containers) <http://python-dependency-injector.ets-labs.org/examples/decoupled-packages.html>`_

Tutorials
---------

Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Expand Up @@ -78,7 +78,7 @@ Key features of the ``Dependency Injector``:
and dictionaries. See :ref:`configuration-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Performance**. Fast. Written in ``Cython``.
- **Typing**. Provides typing stubs, ``mypy``-friendly.
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.

.. code-block:: python
Expand Down
2 changes: 1 addition & 1 deletion docs/introduction/key_features.rst
Expand Up @@ -20,7 +20,7 @@ Key features of the ``Dependency Injector``:
and dictionaries. See :ref:`configuration-provider`.
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
- **Performance**. Fast. Written in ``Cython``.
- **Typing**. Provides typing stubs, ``mypy``-friendly.
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.

The framework stands on two principles:
Expand Down
7 changes: 7 additions & 0 deletions docs/main/changelog.rst
Expand Up @@ -7,6 +7,13 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_

3.44.0
------
- Add native support of the generics to the providers: ``some_provider = providers.Provider[SomeClass]``.
- Deprecate module ``types``.
- Add documentation page on providers typing and ``mypy`` support.
- Update README.

3.43.1
------
- Fix a typo in README.
Expand Down
1 change: 1 addition & 0 deletions docs/providers/index.rst
Expand Up @@ -49,3 +49,4 @@ Providers module API docs - :py:mod:`dependency_injector.providers`
overriding
provided_instance
custom
typing_mypy
58 changes: 58 additions & 0 deletions docs/providers/typing_mypy.rst
@@ -0,0 +1,58 @@
.. _provider-typing:

Typing and mypy
===============

.. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Providers,Typing,Mypy,
Pattern,Example
:description: Dependency Injector providers are mypy-friendly. Providers module goes with the
typing stubs to provide the typing information to ``mypy``, IDEs and editors.

Providers are ``mypy``-friendly.

Providers module goes with the typing stubs. It provides typing information to ``mypy`` and your
IDE.

.. code-block:: python
from dependency_injector import providers
class Animal:
...
class Cat(Animal)
...
provider = providers.Factory(Cat)
if __name__ == '__main__':
animal = provider() # mypy knows that animal is of type "Cat"
You can use ``Provider`` as a generic type. This helps when a provider is an argument of a
function or method.

.. code-block:: python
:emphasize-lines: 12
from dependency_injector import providers
class Animal:
...
class Cat(Animal)
...
provider: providers.Provider[Animal] = providers.Factory(Cat)
if __name__ == '__main__':
animal = provider() # mypy knows that animal is of type "Animal"
2 changes: 1 addition & 1 deletion src/dependency_injector/__init__.py
@@ -1,6 +1,6 @@
"""Top-level package."""

__version__ = '3.43.1'
__version__ = '3.44.0'
"""Version number.
:type: str
Expand Down
20 changes: 14 additions & 6 deletions src/dependency_injector/containers.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 53b7ad0

Please sign in to comment.