mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-06-05 22:23:20 +03:00
Add wiring docs
This commit is contained in:
parent
b4890dcf80
commit
c4b5494b6b
|
@ -77,8 +77,10 @@ Key features of the ``Dependency Injector``:
|
||||||
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
|
- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables
|
||||||
and dictionaries. See :ref:`configuration-provider`.
|
and dictionaries. See :ref:`configuration-provider`.
|
||||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||||
- **Performance**. Fast. Written in ``Cython``.
|
- **Wiring**. Injects container providers into functions and methods. Helps integrating with
|
||||||
|
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
||||||
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
|
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
|
||||||
|
- **Performance**. Fast. Written in ``Cython``.
|
||||||
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
|
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -134,15 +136,16 @@ Explore the documentation to know more about the ``Dependency Injector``.
|
||||||
Contents
|
Contents
|
||||||
--------
|
--------
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
introduction/index
|
introduction/index
|
||||||
examples/index
|
examples/index
|
||||||
tutorials/index
|
tutorials/index
|
||||||
providers/index
|
providers/index
|
||||||
containers/index
|
containers/index
|
||||||
examples-other/index
|
wiring
|
||||||
api/index
|
examples-other/index
|
||||||
main/feedback
|
api/index
|
||||||
main/changelog
|
main/feedback
|
||||||
|
main/changelog
|
||||||
|
|
174
docs/wiring.rst
Normal file
174
docs/wiring.rst
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
.. _wiring:
|
||||||
|
|
||||||
|
Wiring
|
||||||
|
======
|
||||||
|
|
||||||
|
Wiring feature provides a way to inject container providers into the functions and methods.
|
||||||
|
|
||||||
|
To use wiring you need:
|
||||||
|
|
||||||
|
- **Place markers in the code**. Wiring marker specifies what provider to inject,
|
||||||
|
e.g. ``Provide[Container.bar]``. This helps container to find the injections.
|
||||||
|
- **Wire the container with the markers in the code**. Call ``container.wire()``
|
||||||
|
specifying modules and packages you would like to wire it with.
|
||||||
|
- **Use functions and classes as you normally do**. Framework will provide specified injections.
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/wiring/example.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
|
Markers
|
||||||
|
-------
|
||||||
|
|
||||||
|
Wiring feature uses markers to make injections. Injection marker is specified as a default value of
|
||||||
|
a function or method argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector.wiring import Provide
|
||||||
|
|
||||||
|
|
||||||
|
def foo(bar: Bar = Provide[Container.bar]):
|
||||||
|
...
|
||||||
|
|
||||||
|
Specifying an annotation is optional.
|
||||||
|
|
||||||
|
There are two types of markers:
|
||||||
|
|
||||||
|
- ``Provide[foo]`` - call the provider ``foo`` and injects the result
|
||||||
|
- ``Provider[foo]`` - injects the provider ``foo`` itself
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from dependency_injector.wiring import Provider
|
||||||
|
|
||||||
|
|
||||||
|
def foo(bar_provider: Callable[..., Bar] = Provider[Container.bar]):
|
||||||
|
...
|
||||||
|
|
||||||
|
You can use configuration, provided instance and sub-container providers as you normally do.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def foo(token: str = Provide[Container.config.api_token]):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def foo(bar: Bar = Provide[Container.subcontainer.bar]):
|
||||||
|
...
|
||||||
|
|
||||||
|
Wiring with modules and packages
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
To wire a container with a module you need to call ``container.wire(modules=[...])`` method. Argument
|
||||||
|
``modules`` is an iterable of the module objects.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from yourapp import module1, module2
|
||||||
|
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[module1, module2])
|
||||||
|
|
||||||
|
You can wire container with a package. Container walks recursively over package modules.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from yourapp import package1, package2
|
||||||
|
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(packages=[package1, package2])
|
||||||
|
|
||||||
|
Arguments ``modules`` and ``packages`` can be used together.
|
||||||
|
|
||||||
|
When wiring is done functions and methods with the markers are patched to provide injections when called.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def foo(bar: Bar = Provide[Container.bar]):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
||||||
|
|
||||||
|
foo() # <--- Argument "bar" is injected
|
||||||
|
|
||||||
|
Injections are done as keyword arguments.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
foo() # Equivalent to:
|
||||||
|
foo(bar=container.bar())
|
||||||
|
|
||||||
|
Context keyword arguments have a priority over injections.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
foo(bar=Bar()) # Bar() is injected
|
||||||
|
|
||||||
|
To unpatch previously patched functions and methods call ``container.unwire()`` method.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
container.unwire()
|
||||||
|
|
||||||
|
You can use that in testing to re-create and re-wire a container before each test.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class SomeTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.container = Container()
|
||||||
|
self.container.wire(modules=[module1, module2])
|
||||||
|
self.addCleanup(self.container.unwire)
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def container():
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[module1, module2])
|
||||||
|
yield container
|
||||||
|
container.unwire()
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Wiring can take time if you have a large codebase. Consider to persist a container instance and
|
||||||
|
avoid re-wiring between tests.
|
||||||
|
|
||||||
|
Integration with other frameworks
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Wiring feature helps integrate the container providers with other frameworks:
|
||||||
|
Django, Flask, Aiohttp, Sanic, your custom framework, etc.
|
||||||
|
|
||||||
|
With wiring you do not need to change the traditional application structure of your framework.
|
||||||
|
|
||||||
|
1. Create a container and put framework-independent components as providers.
|
||||||
|
2. Place wiring markers in the functions and methods where you want the providers
|
||||||
|
to be injected (Flask or Django views, Aiohttp or Sanic handlers, etc).
|
||||||
|
3. Wire the container with the application.
|
||||||
|
4. Run the application.
|
||||||
|
|
||||||
|
.. literalinclude:: ../examples/wiring/flask_example.py
|
||||||
|
:language: python
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
|
.. disqus::
|
26
examples/wiring/example.py
Normal file
26
examples/wiring/example.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"""Wiring example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import Provide
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
def main(service: Service = Provide[Container.service]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
||||||
|
|
||||||
|
main()
|
29
examples/wiring/flask_example.py
Normal file
29
examples/wiring/flask_example.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
"""Flask wiring example."""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
from dependency_injector.wiring import Provide
|
||||||
|
from flask import Flask, json
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
def index_view(service: Service = Provide[Container.service]) -> str:
|
||||||
|
return json.dumps({'service_id': id(service)})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
container = Container()
|
||||||
|
container.wire(modules=[sys.modules[__name__]])
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.add_url_rule('/', 'index', index_view)
|
||||||
|
app.run()
|
|
@ -4,6 +4,7 @@ max_complexity = 10
|
||||||
exclude = types.py
|
exclude = types.py
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
examples/demo/*: F841
|
examples/demo/*: F841
|
||||||
|
examples/wiring/*: F841
|
||||||
|
|
||||||
[pydocstyle]
|
[pydocstyle]
|
||||||
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
ignore = D100,D101,D102,D105,D106,D107,D203,D213
|
||||||
|
|
Loading…
Reference in New Issue
Block a user