mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-11-24 10:34:01 +03:00
Wiring reengineering (#324)
* Bump version to 4.3.9: FastAPI example * Reengineer wiring * Add @inject decorator * Add .workspace dir to gitignore * Add generic typing for @inject * Add type cast for @inject * Update movie lister example * Update cli application tutorial * Update demo example * Update wiring docs and examples * Update aiohttp example and tutorial * Update multiple containers example * Update single container example * Update decoupled packages example * Update django example * Update asyncio daemon example and tutorial * Update FastAPI example * Update flask example and tutorial * Update sanic example * Add wiring registry * Add new line to .gitignore * Add @inject to the test samples * Fix flake8 errors
This commit is contained in:
parent
bece33fc21
commit
ae3024588c
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -69,3 +69,6 @@ src/dependency_injector/containers/*.h
|
||||||
src/dependency_injector/containers/*.so
|
src/dependency_injector/containers/*.so
|
||||||
src/dependency_injector/providers/*.h
|
src/dependency_injector/providers/*.h
|
||||||
src/dependency_injector/providers/*.so
|
src/dependency_injector/providers/*.so
|
||||||
|
|
||||||
|
# Workspace for samples
|
||||||
|
.workspace/
|
||||||
|
|
|
@ -68,7 +68,7 @@ Key features of the ``Dependency Injector``:
|
||||||
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
|
or process pool, etc. Can be used for per-function execution scope in tandem with wiring.
|
||||||
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
|
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc.
|
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc.
|
||||||
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
|
See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
|
||||||
- **Typing**. Provides typing stubs, ``mypy``-friendly.
|
- **Typing**. Provides typing stubs, ``mypy``-friendly.
|
||||||
See `Typing and mypy <https://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
|
See `Typing and mypy <https://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
|
||||||
|
@ -78,7 +78,7 @@ Key features of the ``Dependency Injector``:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
@ -97,6 +97,7 @@ Key features of the ``Dependency Injector``:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]):
|
def main(service: Service = Provide[Container.service]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
79
docs/examples/fastapi.rst
Normal file
79
docs/examples/fastapi.rst
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
.. _fastapi-example:
|
||||||
|
|
||||||
|
FastAPI example
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. meta::
|
||||||
|
:keywords: Python,Dependency Injection,FastAPI,Example
|
||||||
|
:description: This example demonstrates a usage of the FastAPI and Dependency Injector.
|
||||||
|
|
||||||
|
|
||||||
|
This example shows how to use ``Dependency Injector`` with `FastAPI <https://fastapi.tiangolo.com/>`_.
|
||||||
|
|
||||||
|
The example application is a REST API that searches for funny GIFs on the `Giphy <https://giphy.com/>`_.
|
||||||
|
|
||||||
|
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
|
||||||
|
|
||||||
|
Application structure
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Application has next structure:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
./
|
||||||
|
├── giphynavigator/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── application.py
|
||||||
|
│ ├── containers.py
|
||||||
|
│ ├── endpoints.py
|
||||||
|
│ ├── giphy.py
|
||||||
|
│ ├── services.py
|
||||||
|
│ └── tests.py
|
||||||
|
├── config.yml
|
||||||
|
└── requirements.txt
|
||||||
|
|
||||||
|
Container
|
||||||
|
---------
|
||||||
|
|
||||||
|
Declarative container is defined in ``giphynavigator/containers.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/containers.py
|
||||||
|
:language: python
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
Endpoint has a dependency on search service. There are also some config options that are used as default values.
|
||||||
|
The dependencies are injected using :ref:`wiring` feature.
|
||||||
|
|
||||||
|
Listing of ``giphynavigator/endpoints.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/endpoints.py
|
||||||
|
:language: python
|
||||||
|
|
||||||
|
Application factory
|
||||||
|
-------------------
|
||||||
|
Application factory creates container, wires it with the ``endpoints`` module, creates
|
||||||
|
``FastAPI`` app, and setup routes.
|
||||||
|
|
||||||
|
Listing of ``giphynavigator/application.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/application.py
|
||||||
|
:language: python
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
|
||||||
|
Tests use :ref:`provider-overriding` feature to replace giphy client with a mock ``giphynavigator/tests.py``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../examples/miniapps/fastapi/giphynavigator/tests.py
|
||||||
|
:language: python
|
||||||
|
:emphasize-lines: 29,57,72
|
||||||
|
|
||||||
|
Sources
|
||||||
|
-------
|
||||||
|
|
||||||
|
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi>`_.
|
||||||
|
|
||||||
|
.. disqus::
|
|
@ -17,5 +17,6 @@ Explore the examples to see the ``Dependency Injector`` in action.
|
||||||
flask
|
flask
|
||||||
aiohttp
|
aiohttp
|
||||||
sanic
|
sanic
|
||||||
|
fastapi
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -77,7 +77,7 @@ Key features of the ``Dependency Injector``:
|
||||||
See :ref:`resource-provider`.
|
See :ref:`resource-provider`.
|
||||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, 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``.
|
- **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.
|
||||||
|
@ -85,7 +85,7 @@ Key features of the ``Dependency Injector``:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
@ -104,6 +104,7 @@ Key features of the ``Dependency Injector``:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]):
|
def main(service: Service = Provide[Container.service]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ the dependency.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
class Container(containers.DeclarativeContainer):
|
class Container(containers.DeclarativeContainer):
|
||||||
|
@ -181,6 +181,7 @@ the dependency.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]):
|
def main(service: Service = Provide[Container.service]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -284,6 +285,7 @@ Choose one of the following as a next step:
|
||||||
- :ref:`flask-example`
|
- :ref:`flask-example`
|
||||||
- :ref:`aiohttp-example`
|
- :ref:`aiohttp-example`
|
||||||
- :ref:`sanic-example`
|
- :ref:`sanic-example`
|
||||||
|
- :ref:`fastapi-example`
|
||||||
- Pass the tutorials:
|
- Pass the tutorials:
|
||||||
- :ref:`flask-tutorial`
|
- :ref:`flask-tutorial`
|
||||||
- :ref:`aiohttp-tutorial`
|
- :ref:`aiohttp-tutorial`
|
||||||
|
|
|
@ -23,7 +23,7 @@ Key features of the ``Dependency Injector``:
|
||||||
See :ref:`resource-provider`.
|
See :ref:`resource-provider`.
|
||||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
|
||||||
other frameworks: Django, Flask, Aiohttp, etc. See :ref:`wiring`.
|
other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, 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``.
|
- **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.
|
||||||
|
|
|
@ -7,6 +7,10 @@ that were made in every particular version.
|
||||||
From version 0.7.6 *Dependency Injector* framework strictly
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
follows `Semantic versioning`_
|
||||||
|
|
||||||
|
4.3.9
|
||||||
|
-----
|
||||||
|
- Add ``FastAPI`` example.
|
||||||
|
|
||||||
4.3.8
|
4.3.8
|
||||||
-----
|
-----
|
||||||
- Add a hotfix to support wiring for ``FastAPI`` endpoints.
|
- Add a hotfix to support wiring for ``FastAPI`` endpoints.
|
||||||
|
|
|
@ -216,7 +216,7 @@ execution scope. For doing this you need to use additional ``Closing`` marker fr
|
||||||
.. literalinclude:: ../../examples/wiring/flask_resource_closing.py
|
.. literalinclude:: ../../examples/wiring/flask_resource_closing.py
|
||||||
:language: python
|
:language: python
|
||||||
:lines: 3-
|
:lines: 3-
|
||||||
:emphasize-lines: 23
|
:emphasize-lines: 24
|
||||||
|
|
||||||
Framework initializes and injects the resource into the function. With the ``Closing`` marker
|
Framework initializes and injects the resource into the function. With the ``Closing`` marker
|
||||||
framework calls resource ``shutdown()`` method when function execution is over.
|
framework calls resource ``shutdown()`` method when function execution is over.
|
||||||
|
|
|
@ -526,17 +526,18 @@ the ``index`` handler. We will use :ref:`wiring` feature.
|
||||||
Edit ``handlers.py``:
|
Edit ``handlers.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 4-7,10-13,17
|
:emphasize-lines: 4-7,10-14,18
|
||||||
|
|
||||||
"""Handlers module."""
|
"""Handlers module."""
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
request: web.Request,
|
request: web.Request,
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
|
@ -645,17 +646,18 @@ Let's make some refactoring. We will move these values to the config.
|
||||||
Edit ``handlers.py``:
|
Edit ``handlers.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 13-14,16-17
|
:emphasize-lines: 14-15,17-18
|
||||||
|
|
||||||
"""Handlers module."""
|
"""Handlers module."""
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
request: web.Request,
|
request: web.Request,
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
|
@ -821,11 +823,11 @@ You should see:
|
||||||
giphynavigator/application.py 12 0 100%
|
giphynavigator/application.py 12 0 100%
|
||||||
giphynavigator/containers.py 6 0 100%
|
giphynavigator/containers.py 6 0 100%
|
||||||
giphynavigator/giphy.py 14 9 36%
|
giphynavigator/giphy.py 14 9 36%
|
||||||
giphynavigator/handlers.py 9 0 100%
|
giphynavigator/handlers.py 10 0 100%
|
||||||
giphynavigator/services.py 9 1 89%
|
giphynavigator/services.py 9 1 89%
|
||||||
giphynavigator/tests.py 37 0 100%
|
giphynavigator/tests.py 37 0 100%
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
TOTAL 87 10 89%
|
TOTAL 88 10 89%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -442,18 +442,19 @@ and call the ``run()`` method. We will use :ref:`wiring` feature.
|
||||||
Edit ``__main__.py``:
|
Edit ``__main__.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 3-7,11-12,19
|
:emphasize-lines: 3-7,11-13,20
|
||||||
|
|
||||||
"""Main module."""
|
"""Main module."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .dispatcher import Dispatcher
|
from .dispatcher import Dispatcher
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
||||||
dispatcher.run()
|
dispatcher.run()
|
||||||
|
|
||||||
|
@ -992,14 +993,14 @@ You should see:
|
||||||
Name Stmts Miss Cover
|
Name Stmts Miss Cover
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
monitoringdaemon/__init__.py 0 0 100%
|
monitoringdaemon/__init__.py 0 0 100%
|
||||||
monitoringdaemon/__main__.py 12 12 0%
|
monitoringdaemon/__main__.py 13 13 0%
|
||||||
monitoringdaemon/containers.py 11 0 100%
|
monitoringdaemon/containers.py 11 0 100%
|
||||||
monitoringdaemon/dispatcher.py 44 5 89%
|
monitoringdaemon/dispatcher.py 44 5 89%
|
||||||
monitoringdaemon/http.py 6 3 50%
|
monitoringdaemon/http.py 6 3 50%
|
||||||
monitoringdaemon/monitors.py 23 1 96%
|
monitoringdaemon/monitors.py 23 1 96%
|
||||||
monitoringdaemon/tests.py 37 0 100%
|
monitoringdaemon/tests.py 37 0 100%
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
TOTAL 133 21 84%
|
TOTAL 134 22 84%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -575,18 +575,19 @@ Let's inject the ``lister`` into the ``main()`` function.
|
||||||
Edit ``__main__.py``:
|
Edit ``__main__.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 3-7,11,18
|
:emphasize-lines: 3-7,11-12,19
|
||||||
|
|
||||||
"""Main module."""
|
"""Main module."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .listers import MovieLister
|
from .listers import MovieLister
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -606,18 +607,19 @@ Francis Lawrence and movies released in 2016.
|
||||||
Edit ``__main__.py``:
|
Edit ``__main__.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 12-18
|
:emphasize-lines: 13-19
|
||||||
|
|
||||||
"""Main module."""
|
"""Main module."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .listers import MovieLister
|
from .listers import MovieLister
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||||
print('Francis Lawrence movies:')
|
print('Francis Lawrence movies:')
|
||||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||||
|
@ -861,18 +863,19 @@ Now we need to read the value of the ``config.finder.type`` option from the envi
|
||||||
Edit ``__main__.py``:
|
Edit ``__main__.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 24
|
:emphasize-lines: 25
|
||||||
|
|
||||||
"""Main module."""
|
"""Main module."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .listers import MovieLister
|
from .listers import MovieLister
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||||
print('Francis Lawrence movies:')
|
print('Francis Lawrence movies:')
|
||||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||||
|
@ -1023,14 +1026,14 @@ You should see:
|
||||||
Name Stmts Miss Cover
|
Name Stmts Miss Cover
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
movies/__init__.py 0 0 100%
|
movies/__init__.py 0 0 100%
|
||||||
movies/__main__.py 17 17 0%
|
movies/__main__.py 18 18 0%
|
||||||
movies/containers.py 9 0 100%
|
movies/containers.py 9 0 100%
|
||||||
movies/entities.py 7 1 86%
|
movies/entities.py 7 1 86%
|
||||||
movies/finders.py 26 13 50%
|
movies/finders.py 26 13 50%
|
||||||
movies/listers.py 8 0 100%
|
movies/listers.py 8 0 100%
|
||||||
movies/tests.py 24 0 100%
|
movies/tests.py 24 0 100%
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
TOTAL 91 31 66%
|
TOTAL 92 32 65%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -707,17 +707,19 @@ Let's inject ``SearchService`` into the ``index`` view. We will use :ref:`Wiring
|
||||||
Edit ``views.py``:
|
Edit ``views.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 4,6-7,10,14
|
:emphasize-lines: 4,6-7,10-11,15
|
||||||
|
:emphasize-lines: 4,6-7,10-11,15
|
||||||
|
|
||||||
"""Views module."""
|
"""Views module."""
|
||||||
|
|
||||||
from flask import request, render_template
|
from flask import request, render_template
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index(search_service: SearchService = Provide[Container.search_service]):
|
def index(search_service: SearchService = Provide[Container.search_service]):
|
||||||
query = request.args.get('query', 'Dependency Injector')
|
query = request.args.get('query', 'Dependency Injector')
|
||||||
limit = request.args.get('limit', 10, int)
|
limit = request.args.get('limit', 10, int)
|
||||||
|
@ -783,17 +785,18 @@ Let's make some refactoring. We will move these values to the config.
|
||||||
Edit ``views.py``:
|
Edit ``views.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
:emphasize-lines: 10-16
|
:emphasize-lines: 11-17
|
||||||
|
|
||||||
"""Views module."""
|
"""Views module."""
|
||||||
|
|
||||||
from flask import request, render_template
|
from flask import request, render_template
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index(
|
def index(
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
default_query: str = Provide[Container.config.default.query],
|
default_query: str = Provide[Container.config.default.query],
|
||||||
|
@ -972,9 +975,9 @@ You should see:
|
||||||
githubnavigator/containers.py 7 0 100%
|
githubnavigator/containers.py 7 0 100%
|
||||||
githubnavigator/services.py 14 0 100%
|
githubnavigator/services.py 14 0 100%
|
||||||
githubnavigator/tests.py 34 0 100%
|
githubnavigator/tests.py 34 0 100%
|
||||||
githubnavigator/views.py 9 0 100%
|
githubnavigator/views.py 10 0 100%
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
TOTAL 79 0 100%
|
TOTAL 80 0 100%
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ Wiring feature provides a way to inject container providers into the functions a
|
||||||
|
|
||||||
To use wiring you need:
|
To use wiring you need:
|
||||||
|
|
||||||
- **Place markers in the code**. Wiring marker specifies what provider to inject,
|
- **Place @inject decorator**. Decorator ``@inject`` injects the dependencies.
|
||||||
|
- **Place markers**. Wiring marker specifies what dependency to inject,
|
||||||
e.g. ``Provide[Container.bar]``. This helps container to find the injections.
|
e.g. ``Provide[Container.bar]``. This helps container to find the injections.
|
||||||
- **Wire the container with the markers in the code**. Call ``container.wire()``
|
- **Wire the container with the markers in the code**. Call ``container.wire()``
|
||||||
specifying modules and packages you would like to wire it with.
|
specifying modules and packages you would like to wire it with.
|
||||||
|
@ -25,9 +26,10 @@ a function or method argument:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(bar: Bar = Provide[Container.bar]):
|
def foo(bar: Bar = Provide[Container.bar]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -40,9 +42,10 @@ There are two types of markers:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from dependency_injector.wiring import Provider
|
from dependency_injector.wiring import inject, Provider
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(bar_provider: Callable[..., Bar] = Provider[Container.bar]):
|
def foo(bar_provider: Callable[..., Bar] = Provider[Container.bar]):
|
||||||
bar = bar_provider()
|
bar = bar_provider()
|
||||||
...
|
...
|
||||||
|
@ -51,18 +54,22 @@ You can use configuration, provided instance and sub-container providers as you
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(token: str = Provide[Container.config.api_token]):
|
def foo(token: str = Provide[Container.config.api_token]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
|
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
|
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(bar: Bar = Provide[Container.subcontainer.bar]):
|
def foo(bar: Bar = Provide[Container.subcontainer.bar]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -100,6 +107,7 @@ When wiring is done functions and methods with the markers are patched to provid
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@inject
|
||||||
def foo(bar: Bar = Provide[Container.bar]):
|
def foo(bar: Bar = Provide[Container.bar]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -201,5 +209,6 @@ Take a look at other application examples:
|
||||||
- :ref:`flask-example`
|
- :ref:`flask-example`
|
||||||
- :ref:`aiohttp-example`
|
- :ref:`aiohttp-example`
|
||||||
- :ref:`sanic-example`
|
- :ref:`sanic-example`
|
||||||
|
- :ref:`fastapi-example`
|
||||||
|
|
||||||
.. disqus::
|
.. disqus::
|
||||||
|
|
|
@ -2,7 +2,7 @@ import sys
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from after import ApiClient, Service
|
from after import ApiClient, Service
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ class Container(containers.DeclarativeContainer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]):
|
def main(service: Service = Provide[Container.service]):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -111,8 +111,8 @@ The output should be something like:
|
||||||
giphynavigator/application.py 12 0 100%
|
giphynavigator/application.py 12 0 100%
|
||||||
giphynavigator/containers.py 6 0 100%
|
giphynavigator/containers.py 6 0 100%
|
||||||
giphynavigator/giphy.py 14 9 36%
|
giphynavigator/giphy.py 14 9 36%
|
||||||
giphynavigator/handlers.py 9 0 100%
|
giphynavigator/handlers.py 10 0 100%
|
||||||
giphynavigator/services.py 9 1 89%
|
giphynavigator/services.py 9 1 89%
|
||||||
giphynavigator/tests.py 37 0 100%
|
giphynavigator/tests.py 37 0 100%
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
TOTAL 87 10 89%
|
TOTAL 88 10 89%
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
"""Handlers module."""
|
"""Handlers module."""
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
request: web.Request,
|
request: web.Request,
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import UserService, AuthService, PhotoService
|
from .services import UserService, AuthService, PhotoService
|
||||||
from .containers import Application
|
from .containers import Application
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(
|
def main(
|
||||||
email: str,
|
email: str,
|
||||||
password: str,
|
password: str,
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import UserService, AuthService, PhotoService
|
from .services import UserService, AuthService, PhotoService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(
|
def main(
|
||||||
email: str,
|
email: str,
|
||||||
password: str,
|
password: str,
|
||||||
|
|
|
@ -76,11 +76,11 @@ The output should be something like:
|
||||||
Name Stmts Miss Cover
|
Name Stmts Miss Cover
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
monitoringdaemon/__init__.py 0 0 100%
|
monitoringdaemon/__init__.py 0 0 100%
|
||||||
monitoringdaemon/__main__.py 12 12 0%
|
monitoringdaemon/__main__.py 13 13 0%
|
||||||
monitoringdaemon/containers.py 11 0 100%
|
monitoringdaemon/containers.py 11 0 100%
|
||||||
monitoringdaemon/dispatcher.py 44 5 89%
|
monitoringdaemon/dispatcher.py 44 5 89%
|
||||||
monitoringdaemon/http.py 6 3 50%
|
monitoringdaemon/http.py 6 3 50%
|
||||||
monitoringdaemon/monitors.py 23 1 96%
|
monitoringdaemon/monitors.py 23 1 96%
|
||||||
monitoringdaemon/tests.py 37 0 100%
|
monitoringdaemon/tests.py 37 0 100%
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
TOTAL 133 21 84%
|
TOTAL 134 22 84%
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .dispatcher import Dispatcher
|
from .dispatcher import Dispatcher
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
||||||
dispatcher.run()
|
dispatcher.run()
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .user.repositories import UserRepository
|
from .user.repositories import UserRepository
|
||||||
from .photo.repositories import PhotoRepository
|
from .photo.repositories import PhotoRepository
|
||||||
|
@ -10,6 +10,7 @@ from .analytics.services import AggregationService
|
||||||
from .containers import ApplicationContainer
|
from .containers import ApplicationContainer
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(
|
def main(
|
||||||
user_repository: UserRepository = Provide[
|
user_repository: UserRepository = Provide[
|
||||||
ApplicationContainer.user_package.user_repository
|
ApplicationContainer.user_package.user_repository
|
||||||
|
|
|
@ -108,6 +108,6 @@ The output should be something like:
|
||||||
web/apps.py 7 0 100%
|
web/apps.py 7 0 100%
|
||||||
web/tests.py 28 0 100%
|
web/tests.py 28 0 100%
|
||||||
web/urls.py 3 0 100%
|
web/urls.py 3 0 100%
|
||||||
web/views.py 11 0 100%
|
web/views.py 12 0 100%
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
TOTAL 120 10 92%
|
TOTAL 121 10 92%
|
||||||
|
|
|
@ -4,12 +4,13 @@ from typing import List
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from githubnavigator.containers import Container
|
from githubnavigator.containers import Container
|
||||||
from githubnavigator.services import SearchService
|
from githubnavigator.services import SearchService
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index(
|
def index(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
|
|
|
@ -113,9 +113,9 @@ The output should be something like:
|
||||||
giphynavigator/__init__.py 0 0 100%
|
giphynavigator/__init__.py 0 0 100%
|
||||||
giphynavigator/application.py 13 0 100%
|
giphynavigator/application.py 13 0 100%
|
||||||
giphynavigator/containers.py 6 0 100%
|
giphynavigator/containers.py 6 0 100%
|
||||||
giphynavigator/endpoints.py 5 0 100%
|
giphynavigator/endpoints.py 6 0 100%
|
||||||
giphynavigator/giphy.py 14 9 36%
|
giphynavigator/giphy.py 14 9 36%
|
||||||
giphynavigator/services.py 9 1 89%
|
giphynavigator/services.py 9 1 89%
|
||||||
giphynavigator/tests.py 38 0 100%
|
giphynavigator/tests.py 38 0 100%
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
TOTAL 85 10 88%
|
TOTAL 86 10 88%
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
"""Endpoints module."""
|
"""Endpoints module."""
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
query: str = Provide[Container.config.default.query],
|
query: str = Provide[Container.config.default.query],
|
||||||
limit: int = Provide[Container.config.default.limit.as_int()],
|
limit: int = Provide[Container.config.default.limit.as_int()],
|
||||||
|
|
|
@ -95,6 +95,6 @@ The output should be something like:
|
||||||
githubnavigator/containers.py 7 0 100%
|
githubnavigator/containers.py 7 0 100%
|
||||||
githubnavigator/services.py 14 0 100%
|
githubnavigator/services.py 14 0 100%
|
||||||
githubnavigator/tests.py 34 0 100%
|
githubnavigator/tests.py 34 0 100%
|
||||||
githubnavigator/views.py 9 0 100%
|
githubnavigator/views.py 10 0 100%
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
TOTAL 79 0 100%
|
TOTAL 80 0 100%
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
"""Views module."""
|
"""Views module."""
|
||||||
|
|
||||||
from flask import request, render_template
|
from flask import request, render_template
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index(
|
def index(
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
default_query: str = Provide[Container.config.default.query],
|
default_query: str = Provide[Container.config.default.query],
|
||||||
|
|
|
@ -68,11 +68,11 @@ The output should be something like:
|
||||||
Name Stmts Miss Cover
|
Name Stmts Miss Cover
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
movies/__init__.py 0 0 100%
|
movies/__init__.py 0 0 100%
|
||||||
movies/__main__.py 17 17 0%
|
movies/__main__.py 18 18 0%
|
||||||
movies/containers.py 9 0 100%
|
movies/containers.py 9 0 100%
|
||||||
movies/entities.py 7 1 86%
|
movies/entities.py 7 1 86%
|
||||||
movies/finders.py 26 13 50%
|
movies/finders.py 26 13 50%
|
||||||
movies/listers.py 8 0 100%
|
movies/listers.py 8 0 100%
|
||||||
movies/tests.py 24 0 100%
|
movies/tests.py 24 0 100%
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
TOTAL 91 31 66%
|
TOTAL 92 32 65%
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .listers import MovieLister
|
from .listers import MovieLister
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||||
print('Francis Lawrence movies:')
|
print('Francis Lawrence movies:')
|
||||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||||
|
|
|
@ -112,8 +112,8 @@ The output should be something like:
|
||||||
giphynavigator/application.py 12 0 100%
|
giphynavigator/application.py 12 0 100%
|
||||||
giphynavigator/containers.py 6 0 100%
|
giphynavigator/containers.py 6 0 100%
|
||||||
giphynavigator/giphy.py 14 9 36%
|
giphynavigator/giphy.py 14 9 36%
|
||||||
giphynavigator/handlers.py 10 0 100%
|
giphynavigator/handlers.py 11 0 100%
|
||||||
giphynavigator/services.py 9 1 89%
|
giphynavigator/services.py 9 1 89%
|
||||||
giphynavigator/tests.py 34 0 100%
|
giphynavigator/tests.py 34 0 100%
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
TOTAL 89 14 84%
|
TOTAL 90 14 84%
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import HTTPResponse, json
|
from sanic.response import HTTPResponse, json
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from .services import SearchService
|
from .services import SearchService
|
||||||
from .containers import Container
|
from .containers import Container
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
async def index(
|
async def index(
|
||||||
request: Request,
|
request: Request,
|
||||||
search_service: SearchService = Provide[Container.search_service],
|
search_service: SearchService = Provide[Container.search_service],
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
@ -15,6 +15,7 @@ class Container(containers.DeclarativeContainer):
|
||||||
service = providers.Factory(Service)
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def main(service: Service = Provide[Container.service]) -> None:
|
def main(service: Service = Provide[Container.service]) -> None:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
from flask import Flask, json
|
from flask import Flask, json
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ class Container(containers.DeclarativeContainer):
|
||||||
service = providers.Factory(Service)
|
service = providers.Factory(Service)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index_view(service: Service = Provide[Container.service]) -> str:
|
def index_view(service: Service = Provide[Container.service]) -> str:
|
||||||
return json.dumps({'service_id': id(service)})
|
return json.dumps({'service_id': id(service)})
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide, Closing
|
from dependency_injector.wiring import inject, Provide, Closing
|
||||||
from flask import Flask, current_app
|
from flask import Flask, current_app
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ class Container(containers.DeclarativeContainer):
|
||||||
service = providers.Resource(init_service)
|
service = providers.Resource(init_service)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def index_view(service: Service = Closing[Provide[Container.service]]):
|
def index_view(service: Service = Closing[Provide[Container.service]]):
|
||||||
assert service is current_app.container.service()
|
assert service is current_app.container.service()
|
||||||
return 'Hello World!'
|
return 'Hello World!'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Top-level package."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = '4.3.8'
|
__version__ = '4.3.9'
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
|
@ -6,7 +6,19 @@ import importlib
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Optional, Iterable, Callable, Any, Tuple, Dict, Generic, TypeVar, Type, cast
|
from typing import (
|
||||||
|
Optional,
|
||||||
|
Iterable,
|
||||||
|
Iterator,
|
||||||
|
Callable,
|
||||||
|
Any,
|
||||||
|
Tuple,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
|
TypeVar,
|
||||||
|
Type,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
if sys.version_info < (3, 7):
|
if sys.version_info < (3, 7):
|
||||||
from typing import GenericMeta
|
from typing import GenericMeta
|
||||||
|
@ -21,15 +33,35 @@ from . import providers
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'wire',
|
'wire',
|
||||||
'unwire',
|
'unwire',
|
||||||
|
'inject',
|
||||||
'Provide',
|
'Provide',
|
||||||
'Provider',
|
'Provider',
|
||||||
'Closing',
|
'Closing',
|
||||||
)
|
)
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
F = TypeVar('F', bound=Callable[..., Any])
|
||||||
Container = Any
|
Container = Any
|
||||||
|
|
||||||
|
|
||||||
|
class Registry:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._storage = set()
|
||||||
|
|
||||||
|
def add(self, patched: Callable[..., Any]) -> None:
|
||||||
|
self._storage.add(patched)
|
||||||
|
|
||||||
|
def get_from_module(self, module: ModuleType) -> Iterator[Callable[..., Any]]:
|
||||||
|
for patched in self._storage:
|
||||||
|
if patched.__module__ != module.__name__:
|
||||||
|
continue
|
||||||
|
yield patched
|
||||||
|
|
||||||
|
|
||||||
|
_patched_registry = Registry()
|
||||||
|
|
||||||
|
|
||||||
class ProvidersMap:
|
class ProvidersMap:
|
||||||
|
|
||||||
def __init__(self, container):
|
def __init__(self, container):
|
||||||
|
@ -152,7 +184,7 @@ class ProvidersMap:
|
||||||
return providers_map
|
return providers_map
|
||||||
|
|
||||||
|
|
||||||
def wire(
|
def wire( # noqa: C901
|
||||||
container: Container,
|
container: Container,
|
||||||
*,
|
*,
|
||||||
modules: Optional[Iterable[ModuleType]] = None,
|
modules: Optional[Iterable[ModuleType]] = None,
|
||||||
|
@ -179,6 +211,9 @@ def wire(
|
||||||
for method_name, method in inspect.getmembers(member, _is_method):
|
for method_name, method in inspect.getmembers(member, _is_method):
|
||||||
_patch_method(member, method_name, method, providers_map)
|
_patch_method(member, method_name, method, providers_map)
|
||||||
|
|
||||||
|
for patched in _patched_registry.get_from_module(module):
|
||||||
|
_bind_injections(patched, providers_map)
|
||||||
|
|
||||||
|
|
||||||
def unwire(
|
def unwire(
|
||||||
*,
|
*,
|
||||||
|
@ -201,6 +236,17 @@ def unwire(
|
||||||
for method_name, method in inspect.getmembers(member, inspect.isfunction):
|
for method_name, method in inspect.getmembers(member, inspect.isfunction):
|
||||||
_unpatch(member, method_name, method)
|
_unpatch(member, method_name, method)
|
||||||
|
|
||||||
|
for patched in _patched_registry.get_from_module(module):
|
||||||
|
_unbind_injections(patched)
|
||||||
|
|
||||||
|
|
||||||
|
def inject(fn: F) -> F:
|
||||||
|
"""Decorate callable with injecting decorator."""
|
||||||
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
patched = _get_patched(fn, reference_injections, reference_closing)
|
||||||
|
_patched_registry.add(patched)
|
||||||
|
return cast(F, patched)
|
||||||
|
|
||||||
|
|
||||||
def _patch_fn(
|
def _patch_fn(
|
||||||
module: ModuleType,
|
module: ModuleType,
|
||||||
|
@ -208,11 +254,16 @@ def _patch_fn(
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> None:
|
) -> None:
|
||||||
injections, closing = _resolve_injections(fn, providers_map)
|
if not _is_patched(fn):
|
||||||
if not injections:
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
if not reference_injections:
|
||||||
return
|
return
|
||||||
patched = _patch_with_injections(fn, injections, closing)
|
fn = _get_patched(fn, reference_injections, reference_closing)
|
||||||
setattr(module, name, _wrap_patched(patched, fn, injections, closing))
|
_patched_registry.add(fn)
|
||||||
|
|
||||||
|
_bind_injections(fn, providers_map)
|
||||||
|
|
||||||
|
setattr(module, name, fn)
|
||||||
|
|
||||||
|
|
||||||
def _patch_method(
|
def _patch_method(
|
||||||
|
@ -221,28 +272,27 @@ def _patch_method(
|
||||||
method: Callable[..., Any],
|
method: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
providers_map: ProvidersMap,
|
||||||
) -> None:
|
) -> None:
|
||||||
injections, closing = _resolve_injections(method, providers_map)
|
|
||||||
if not injections:
|
|
||||||
return
|
|
||||||
|
|
||||||
if hasattr(cls, '__dict__') \
|
if hasattr(cls, '__dict__') \
|
||||||
and name in cls.__dict__ \
|
and name in cls.__dict__ \
|
||||||
and isinstance(cls.__dict__[name], (classmethod, staticmethod)):
|
and isinstance(cls.__dict__[name], (classmethod, staticmethod)):
|
||||||
method = cls.__dict__[name]
|
method = cls.__dict__[name]
|
||||||
patched = _patch_with_injections(method.__func__, injections, closing)
|
fn = method.__func__
|
||||||
patched = type(method)(patched)
|
|
||||||
else:
|
else:
|
||||||
patched = _patch_with_injections(method, injections, closing)
|
fn = method
|
||||||
|
|
||||||
setattr(cls, name, _wrap_patched(patched, method, injections, closing))
|
if not _is_patched(fn):
|
||||||
|
reference_injections, reference_closing = _fetch_reference_injections(fn)
|
||||||
|
if not reference_injections:
|
||||||
|
return
|
||||||
|
fn = _get_patched(fn, reference_injections, reference_closing)
|
||||||
|
_patched_registry.add(fn)
|
||||||
|
|
||||||
|
_bind_injections(fn, providers_map)
|
||||||
|
|
||||||
def _wrap_patched(patched: Callable[..., Any], original, injections, closing):
|
if isinstance(method, (classmethod, staticmethod)):
|
||||||
patched.__wired__ = True
|
fn = type(method)(fn)
|
||||||
patched.__original__ = original
|
|
||||||
patched.__injections__ = injections
|
setattr(cls, name, fn)
|
||||||
patched.__closing__ = closing
|
|
||||||
return patched
|
|
||||||
|
|
||||||
|
|
||||||
def _unpatch(
|
def _unpatch(
|
||||||
|
@ -250,14 +300,20 @@ def _unpatch(
|
||||||
name: str,
|
name: str,
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if hasattr(module, '__dict__') \
|
||||||
|
and name in module.__dict__ \
|
||||||
|
and isinstance(module.__dict__[name], (classmethod, staticmethod)):
|
||||||
|
method = module.__dict__[name]
|
||||||
|
fn = method.__func__
|
||||||
|
|
||||||
if not _is_patched(fn):
|
if not _is_patched(fn):
|
||||||
return
|
return
|
||||||
setattr(module, name, _get_original_from_patched(fn))
|
|
||||||
|
_unbind_injections(fn)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_injections(
|
def _fetch_reference_injections(
|
||||||
fn: Callable[..., Any],
|
fn: Callable[..., Any],
|
||||||
providers_map: ProvidersMap,
|
|
||||||
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||||
signature = inspect.signature(fn)
|
signature = inspect.signature(fn)
|
||||||
|
|
||||||
|
@ -268,24 +324,33 @@ def _resolve_injections(
|
||||||
continue
|
continue
|
||||||
marker = parameter.default
|
marker = parameter.default
|
||||||
|
|
||||||
closing_modifier = False
|
|
||||||
if isinstance(marker, Closing):
|
if isinstance(marker, Closing):
|
||||||
closing_modifier = True
|
|
||||||
marker = marker.provider
|
marker = marker.provider
|
||||||
|
closing[parameter_name] = marker
|
||||||
|
|
||||||
|
injections[parameter_name] = marker
|
||||||
|
return injections, closing
|
||||||
|
|
||||||
|
|
||||||
|
def _bind_injections(fn: Callable[..., Any], providers_map: ProvidersMap) -> None:
|
||||||
|
for injection, marker in fn.__reference_injections__.items():
|
||||||
provider = providers_map.resolve_provider(marker.provider)
|
provider = providers_map.resolve_provider(marker.provider)
|
||||||
|
|
||||||
if provider is None:
|
if provider is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if closing_modifier:
|
|
||||||
closing[parameter_name] = provider
|
|
||||||
|
|
||||||
if isinstance(marker, Provide):
|
if isinstance(marker, Provide):
|
||||||
injections[parameter_name] = provider
|
fn.__injections__[injection] = provider
|
||||||
elif isinstance(marker, Provider):
|
elif isinstance(marker, Provider):
|
||||||
injections[parameter_name] = provider.provider
|
fn.__injections__[injection] = provider.provider
|
||||||
|
|
||||||
return injections, closing
|
if injection in fn.__reference_closing__:
|
||||||
|
fn.__closing__[injection] = provider
|
||||||
|
|
||||||
|
|
||||||
|
def _unbind_injections(fn: Callable[..., Any]) -> None:
|
||||||
|
fn.__injections__ = {}
|
||||||
|
fn.__closing__ = {}
|
||||||
|
|
||||||
|
|
||||||
def _fetch_modules(package):
|
def _fetch_modules(package):
|
||||||
|
@ -303,26 +368,34 @@ def _is_method(member):
|
||||||
return inspect.ismethod(member) or inspect.isfunction(member)
|
return inspect.ismethod(member) or inspect.isfunction(member)
|
||||||
|
|
||||||
|
|
||||||
def _patch_with_injections(fn, injections, closing):
|
def _get_patched(fn, reference_injections, reference_closing):
|
||||||
if inspect.iscoroutinefunction(fn):
|
if inspect.iscoroutinefunction(fn):
|
||||||
_patched = _get_async_patched(fn, injections, closing)
|
patched = _get_async_patched(fn)
|
||||||
else:
|
else:
|
||||||
_patched = _get_patched(fn, injections, closing)
|
patched = _get_sync_patched(fn)
|
||||||
return _patched
|
|
||||||
|
patched.__wired__ = True
|
||||||
|
patched.__original__ = fn
|
||||||
|
patched.__injections__ = {}
|
||||||
|
patched.__reference_injections__ = reference_injections
|
||||||
|
patched.__closing__ = {}
|
||||||
|
patched.__reference_closing__ = reference_closing
|
||||||
|
|
||||||
|
return patched
|
||||||
|
|
||||||
|
|
||||||
def _get_patched(fn, injections, closing):
|
def _get_sync_patched(fn):
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
def _patched(*args, **kwargs):
|
def _patched(*args, **kwargs):
|
||||||
to_inject = kwargs.copy()
|
to_inject = kwargs.copy()
|
||||||
for injection, provider in injections.items():
|
for injection, provider in _patched.__injections__.items():
|
||||||
if injection not in kwargs \
|
if injection not in kwargs \
|
||||||
or _is_fastapi_default_arg_injection(injection, kwargs):
|
or _is_fastapi_default_arg_injection(injection, kwargs):
|
||||||
to_inject[injection] = provider()
|
to_inject[injection] = provider()
|
||||||
|
|
||||||
result = fn(*args, **to_inject)
|
result = fn(*args, **to_inject)
|
||||||
|
|
||||||
for injection, provider in closing.items():
|
for injection, provider in _patched.__closing__.items():
|
||||||
if injection in kwargs \
|
if injection in kwargs \
|
||||||
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
||||||
continue
|
continue
|
||||||
|
@ -334,18 +407,18 @@ def _get_patched(fn, injections, closing):
|
||||||
return _patched
|
return _patched
|
||||||
|
|
||||||
|
|
||||||
def _get_async_patched(fn, injections, closing):
|
def _get_async_patched(fn):
|
||||||
@functools.wraps(fn)
|
@functools.wraps(fn)
|
||||||
async def _patched(*args, **kwargs):
|
async def _patched(*args, **kwargs):
|
||||||
to_inject = kwargs.copy()
|
to_inject = kwargs.copy()
|
||||||
for injection, provider in injections.items():
|
for injection, provider in _patched.__injections__.items():
|
||||||
if injection not in kwargs \
|
if injection not in kwargs \
|
||||||
or _is_fastapi_default_arg_injection(injection, kwargs):
|
or _is_fastapi_default_arg_injection(injection, kwargs):
|
||||||
to_inject[injection] = provider()
|
to_inject[injection] = provider()
|
||||||
|
|
||||||
result = await fn(*args, **to_inject)
|
result = await fn(*args, **to_inject)
|
||||||
|
|
||||||
for injection, provider in closing.items():
|
for injection, provider in _patched.__closing__.items():
|
||||||
if injection in kwargs \
|
if injection in kwargs \
|
||||||
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
||||||
continue
|
continue
|
||||||
|
@ -366,10 +439,6 @@ def _is_patched(fn):
|
||||||
return getattr(fn, '__wired__', False) is True
|
return getattr(fn, '__wired__', False) is True
|
||||||
|
|
||||||
|
|
||||||
def _get_original_from_patched(fn):
|
|
||||||
return getattr(fn, '__original__')
|
|
||||||
|
|
||||||
|
|
||||||
def _is_declarative_container_instance(instance: Any) -> bool:
|
def _is_declarative_container_instance(instance: Any) -> bool:
|
||||||
return (not isinstance(instance, type)
|
return (not isinstance(instance, type)
|
||||||
and getattr(instance, '__IS_CONTAINER__', False) is True
|
and getattr(instance, '__IS_CONTAINER__', False) is True
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from dependency_injector.wiring import Provide, Provider
|
from dependency_injector.wiring import inject, Provide, Provider
|
||||||
|
|
||||||
from .container import Container, SubContainer
|
from .container import Container, SubContainer
|
||||||
from .service import Service
|
from .service import Service
|
||||||
|
@ -11,30 +11,37 @@ from .service import Service
|
||||||
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
|
|
||||||
|
@inject
|
||||||
def __init__(self, service: Service = Provide[Container.service]):
|
def __init__(self, service: Service = Provide[Container.service]):
|
||||||
self.service = service
|
self.service = service
|
||||||
|
|
||||||
|
@inject
|
||||||
def method(self, service: Service = Provide[Container.service]):
|
def method(self, service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@inject
|
||||||
def class_method(cls, service: Service = Provide[Container.service]):
|
def class_method(cls, service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@inject
|
||||||
def static_method(service: Service = Provide[Container.service]):
|
def static_method(service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_function(service: Service = Provide[Container.service]):
|
def test_function(service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_function_provider(service_provider: Callable[..., Service] = Provider[Container.service]):
|
def test_function_provider(service_provider: Callable[..., Service] = Provider[Container.service]):
|
||||||
service = service_provider()
|
service = service_provider()
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_config_value(
|
def test_config_value(
|
||||||
some_value_int: int = Provide[Container.config.a.b.c.as_int()],
|
some_value_int: int = Provide[Container.config.a.b.c.as_int()],
|
||||||
some_value_str: str = Provide[Container.config.a.b.c.as_(str)],
|
some_value_str: str = Provide[Container.config.a.b.c.as_(str)],
|
||||||
|
@ -43,25 +50,44 @@ def test_config_value(
|
||||||
return some_value_int, some_value_str, some_value_decimal
|
return some_value_int, some_value_str, some_value_decimal
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_provide_provider(service_provider: Callable[..., Service] = Provider[Container.service.provider]):
|
def test_provide_provider(service_provider: Callable[..., Service] = Provider[Container.service.provider]):
|
||||||
service = service_provider()
|
service = service_provider()
|
||||||
return service
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_provided_instance(some_value: int = Provide[Container.service.provided.foo['bar'].call()]):
|
def test_provided_instance(some_value: int = Provide[Container.service.provided.foo['bar'].call()]):
|
||||||
return some_value
|
return some_value
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_subcontainer_provider(some_value: int = Provide[Container.sub.int_object]):
|
def test_subcontainer_provider(some_value: int = Provide[Container.sub.int_object]):
|
||||||
return some_value
|
return some_value
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_config_invariant(some_value: int = Provide[Container.config.option[Container.config.switch]]):
|
def test_config_invariant(some_value: int = Provide[Container.config.option[Container.config.switch]]):
|
||||||
return some_value
|
return some_value
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_provide_from_different_containers(
|
def test_provide_from_different_containers(
|
||||||
service: Service = Provide[Container.service],
|
service: Service = Provide[Container.service],
|
||||||
some_value: int = Provide[SubContainer.int_object],
|
some_value: int = Provide[SubContainer.int_object],
|
||||||
):
|
):
|
||||||
return service, some_value
|
return service, some_value
|
||||||
|
|
||||||
|
|
||||||
|
class ClassDecorator:
|
||||||
|
def __init__(self, fn):
|
||||||
|
self._fn = fn
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return self._fn(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ClassDecorator
|
||||||
|
@inject
|
||||||
|
def test_class_decorator(service: Service = Provide[Container.service]):
|
||||||
|
return service
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from dependency_injector.wiring import Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
|
||||||
from ...container import Container
|
from ...container import Container
|
||||||
from ...service import Service
|
from ...service import Service
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_function(service: Service = Provide[Container.service]):
|
def test_function(service: Service = Provide[Container.service]):
|
||||||
return service
|
return service
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from dependency_injector import containers, providers
|
from dependency_injector import containers, providers
|
||||||
from dependency_injector.wiring import Provide, Closing
|
from dependency_injector.wiring import inject, Provide, Closing
|
||||||
|
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
@ -32,5 +32,6 @@ class Container(containers.DeclarativeContainer):
|
||||||
service = providers.Resource(init_service)
|
service = providers.Resource(init_service)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
def test_function(service: Service = Closing[Provide[Container.service]]):
|
def test_function(service: Service = Closing[Provide[Container.service]]):
|
||||||
return service
|
return service
|
||||||
|
|
|
@ -226,6 +226,10 @@ class WiringTest(unittest.TestCase):
|
||||||
self.assertEqual(result_2.init_counter, 0)
|
self.assertEqual(result_2.init_counter, 0)
|
||||||
self.assertEqual(result_2.shutdown_counter, 0)
|
self.assertEqual(result_2.shutdown_counter, 0)
|
||||||
|
|
||||||
|
def test_class_decorator(self):
|
||||||
|
service = module.test_class_decorator()
|
||||||
|
self.assertIsInstance(service, Service)
|
||||||
|
|
||||||
|
|
||||||
class WiringAndFastAPITest(unittest.TestCase):
|
class WiringAndFastAPITest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user