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/providers/*.h
|
||||
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.
|
||||
See `Resource provider <https://python-dependency-injector.ets-labs.org/providers/resource.html>`_.
|
||||
- **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>`_.
|
||||
- **Typing**. Provides typing stubs, ``mypy``-friendly.
|
||||
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
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -97,6 +97,7 @@ Key features of the ``Dependency Injector``:
|
|||
)
|
||||
|
||||
|
||||
@inject
|
||||
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
|
||||
aiohttp
|
||||
sanic
|
||||
fastapi
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -77,7 +77,7 @@ Key features of the ``Dependency Injector``:
|
|||
See :ref:`resource-provider`.
|
||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||
- **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`.
|
||||
- **Performance**. Fast. Written in ``Cython``.
|
||||
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
|
||||
|
@ -85,7 +85,7 @@ Key features of the ``Dependency Injector``:
|
|||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -104,6 +104,7 @@ Key features of the ``Dependency Injector``:
|
|||
)
|
||||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]):
|
||||
...
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ the dependency.
|
|||
.. code-block:: python
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
@ -181,6 +181,7 @@ the dependency.
|
|||
)
|
||||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]):
|
||||
...
|
||||
|
||||
|
@ -284,6 +285,7 @@ Choose one of the following as a next step:
|
|||
- :ref:`flask-example`
|
||||
- :ref:`aiohttp-example`
|
||||
- :ref:`sanic-example`
|
||||
- :ref:`fastapi-example`
|
||||
- Pass the tutorials:
|
||||
- :ref:`flask-tutorial`
|
||||
- :ref:`aiohttp-tutorial`
|
||||
|
|
|
@ -23,7 +23,7 @@ Key features of the ``Dependency Injector``:
|
|||
See :ref:`resource-provider`.
|
||||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
|
||||
- **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`.
|
||||
- **Performance**. Fast. Written in ``Cython``.
|
||||
- **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
|
||||
follows `Semantic versioning`_
|
||||
|
||||
4.3.9
|
||||
-----
|
||||
- Add ``FastAPI`` example.
|
||||
|
||||
4.3.8
|
||||
-----
|
||||
- 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
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 23
|
||||
:emphasize-lines: 24
|
||||
|
||||
Framework initializes and injects the resource into the function. With the ``Closing`` marker
|
||||
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``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4-7,10-13,17
|
||||
:emphasize-lines: 4-7,10-14,18
|
||||
|
||||
"""Handlers module."""
|
||||
|
||||
from aiohttp import web
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
async def index(
|
||||
request: web.Request,
|
||||
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``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 13-14,16-17
|
||||
:emphasize-lines: 14-15,17-18
|
||||
|
||||
"""Handlers module."""
|
||||
|
||||
from aiohttp import web
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
async def index(
|
||||
request: web.Request,
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
|
@ -821,11 +823,11 @@ You should see:
|
|||
giphynavigator/application.py 12 0 100%
|
||||
giphynavigator/containers.py 6 0 100%
|
||||
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/tests.py 37 0 100%
|
||||
---------------------------------------------------
|
||||
TOTAL 87 10 89%
|
||||
TOTAL 88 10 89%
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -442,18 +442,19 @@ and call the ``run()`` method. We will use :ref:`wiring` feature.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3-7,11-12,19
|
||||
:emphasize-lines: 3-7,11-13,20
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .dispatcher import Dispatcher
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
||||
dispatcher.run()
|
||||
|
||||
|
@ -992,14 +993,14 @@ You should see:
|
|||
Name Stmts Miss Cover
|
||||
----------------------------------------------------
|
||||
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/dispatcher.py 44 5 89%
|
||||
monitoringdaemon/http.py 6 3 50%
|
||||
monitoringdaemon/monitors.py 23 1 96%
|
||||
monitoringdaemon/tests.py 37 0 100%
|
||||
----------------------------------------------------
|
||||
TOTAL 133 21 84%
|
||||
TOTAL 134 22 84%
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -575,18 +575,19 @@ Let's inject the ``lister`` into the ``main()`` function.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 3-7,11,18
|
||||
:emphasize-lines: 3-7,11-12,19
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
...
|
||||
|
||||
|
@ -606,18 +607,19 @@ Francis Lawrence and movies released in 2016.
|
|||
Edit ``__main__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 12-18
|
||||
:emphasize-lines: 13-19
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
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``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 24
|
||||
:emphasize-lines: 25
|
||||
|
||||
"""Main module."""
|
||||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
for movie in lister.movies_directed_by('Francis Lawrence'):
|
||||
|
@ -1023,14 +1026,14 @@ You should see:
|
|||
Name Stmts Miss Cover
|
||||
------------------------------------------
|
||||
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/entities.py 7 1 86%
|
||||
movies/finders.py 26 13 50%
|
||||
movies/listers.py 8 0 100%
|
||||
movies/tests.py 24 0 100%
|
||||
------------------------------------------
|
||||
TOTAL 91 31 66%
|
||||
TOTAL 92 32 65%
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -707,17 +707,19 @@ Let's inject ``SearchService`` into the ``index`` view. We will use :ref:`Wiring
|
|||
Edit ``views.py``:
|
||||
|
||||
.. 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."""
|
||||
|
||||
from flask import request, render_template
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def index(search_service: SearchService = Provide[Container.search_service]):
|
||||
query = request.args.get('query', 'Dependency Injector')
|
||||
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``:
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 10-16
|
||||
:emphasize-lines: 11-17
|
||||
|
||||
"""Views module."""
|
||||
|
||||
from flask import request, render_template
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def index(
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
default_query: str = Provide[Container.config.default.query],
|
||||
|
@ -972,9 +975,9 @@ You should see:
|
|||
githubnavigator/containers.py 7 0 100%
|
||||
githubnavigator/services.py 14 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::
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ Wiring feature provides a way to inject container providers into the functions a
|
|||
|
||||
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.
|
||||
- **Wire the container with the markers in the code**. Call ``container.wire()``
|
||||
specifying modules and packages you would like to wire it with.
|
||||
|
@ -25,9 +26,10 @@ a function or method argument:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
@inject
|
||||
def foo(bar: Bar = Provide[Container.bar]):
|
||||
...
|
||||
|
||||
|
@ -40,9 +42,10 @@ There are two types of markers:
|
|||
|
||||
.. 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]):
|
||||
bar = bar_provider()
|
||||
...
|
||||
|
@ -51,18 +54,22 @@ You can use configuration, provided instance and sub-container providers as you
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
@inject
|
||||
def foo(token: str = Provide[Container.config.api_token]):
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
|
||||
...
|
||||
|
||||
|
||||
@inject
|
||||
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
|
||||
|
||||
@inject
|
||||
def foo(bar: Bar = Provide[Container.bar]):
|
||||
...
|
||||
|
||||
|
@ -201,5 +209,6 @@ Take a look at other application examples:
|
|||
- :ref:`flask-example`
|
||||
- :ref:`aiohttp-example`
|
||||
- :ref:`sanic-example`
|
||||
- :ref:`fastapi-example`
|
||||
|
||||
.. disqus::
|
||||
|
|
|
@ -2,7 +2,7 @@ import sys
|
|||
from unittest import mock
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from after import ApiClient, Service
|
||||
|
||||
|
@ -23,6 +23,7 @@ class Container(containers.DeclarativeContainer):
|
|||
)
|
||||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]):
|
||||
...
|
||||
|
||||
|
|
|
@ -111,8 +111,8 @@ The output should be something like:
|
|||
giphynavigator/application.py 12 0 100%
|
||||
giphynavigator/containers.py 6 0 100%
|
||||
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/tests.py 37 0 100%
|
||||
---------------------------------------------------
|
||||
TOTAL 87 10 89%
|
||||
TOTAL 88 10 89%
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
"""Handlers module."""
|
||||
|
||||
from aiohttp import web
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
async def index(
|
||||
request: web.Request,
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import UserService, AuthService, PhotoService
|
||||
from .containers import Application
|
||||
|
||||
|
||||
@inject
|
||||
def main(
|
||||
email: str,
|
||||
password: str,
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import UserService, AuthService, PhotoService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(
|
||||
email: str,
|
||||
password: str,
|
||||
|
|
|
@ -76,11 +76,11 @@ The output should be something like:
|
|||
Name Stmts Miss Cover
|
||||
----------------------------------------------------
|
||||
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/dispatcher.py 44 5 89%
|
||||
monitoringdaemon/http.py 6 3 50%
|
||||
monitoringdaemon/monitors.py 23 1 96%
|
||||
monitoringdaemon/tests.py 37 0 100%
|
||||
----------------------------------------------------
|
||||
TOTAL 133 21 84%
|
||||
TOTAL 134 22 84%
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .dispatcher import Dispatcher
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
|
||||
dispatcher.run()
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .user.repositories import UserRepository
|
||||
from .photo.repositories import PhotoRepository
|
||||
|
@ -10,6 +10,7 @@ from .analytics.services import AggregationService
|
|||
from .containers import ApplicationContainer
|
||||
|
||||
|
||||
@inject
|
||||
def main(
|
||||
user_repository: UserRepository = Provide[
|
||||
ApplicationContainer.user_package.user_repository
|
||||
|
|
|
@ -108,6 +108,6 @@ The output should be something like:
|
|||
web/apps.py 7 0 100%
|
||||
web/tests.py 28 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.shortcuts import render
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from githubnavigator.containers import Container
|
||||
from githubnavigator.services import SearchService
|
||||
|
||||
|
||||
@inject
|
||||
def index(
|
||||
request: HttpRequest,
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
|
|
|
@ -113,9 +113,9 @@ The output should be something like:
|
|||
giphynavigator/__init__.py 0 0 100%
|
||||
giphynavigator/application.py 13 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/services.py 9 1 89%
|
||||
giphynavigator/tests.py 38 0 100%
|
||||
---------------------------------------------------
|
||||
TOTAL 85 10 88%
|
||||
TOTAL 86 10 88%
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
"""Endpoints module."""
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
async def index(
|
||||
query: str = Provide[Container.config.default.query],
|
||||
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/services.py 14 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."""
|
||||
|
||||
from flask import request, render_template
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def index(
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
default_query: str = Provide[Container.config.default.query],
|
||||
|
|
|
@ -68,11 +68,11 @@ The output should be something like:
|
|||
Name Stmts Miss Cover
|
||||
------------------------------------------
|
||||
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/entities.py 7 1 86%
|
||||
movies/finders.py 26 13 50%
|
||||
movies/listers.py 8 0 100%
|
||||
movies/tests.py 24 0 100%
|
||||
------------------------------------------
|
||||
TOTAL 91 31 66%
|
||||
TOTAL 92 32 65%
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import sys
|
||||
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .listers import MovieLister
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
def main(lister: MovieLister = Provide[Container.lister]) -> None:
|
||||
print('Francis Lawrence movies:')
|
||||
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/containers.py 6 0 100%
|
||||
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/tests.py 34 0 100%
|
||||
---------------------------------------------------
|
||||
TOTAL 89 14 84%
|
||||
TOTAL 90 14 84%
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
from sanic.request import Request
|
||||
from sanic.response import HTTPResponse, json
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
from .services import SearchService
|
||||
from .containers import Container
|
||||
|
||||
|
||||
@inject
|
||||
async def index(
|
||||
request: Request,
|
||||
search_service: SearchService = Provide[Container.search_service],
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -15,6 +15,7 @@ class Container(containers.DeclarativeContainer):
|
|||
service = providers.Factory(Service)
|
||||
|
||||
|
||||
@inject
|
||||
def main(service: Service = Provide[Container.service]) -> None:
|
||||
...
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import sys
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide
|
||||
from dependency_injector.wiring import inject, Provide
|
||||
from flask import Flask, json
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@ class Container(containers.DeclarativeContainer):
|
|||
service = providers.Factory(Service)
|
||||
|
||||
|
||||
@inject
|
||||
def index_view(service: Service = Provide[Container.service]) -> str:
|
||||
return json.dumps({'service_id': id(service)})
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import sys
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -22,6 +22,7 @@ class Container(containers.DeclarativeContainer):
|
|||
service = providers.Resource(init_service)
|
||||
|
||||
|
||||
@inject
|
||||
def index_view(service: Service = Closing[Provide[Container.service]]):
|
||||
assert service is current_app.container.service()
|
||||
return 'Hello World!'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Top-level package."""
|
||||
|
||||
__version__ = '4.3.8'
|
||||
__version__ = '4.3.9'
|
||||
"""Version number.
|
||||
|
||||
:type: str
|
||||
|
|
|
@ -6,7 +6,19 @@ import importlib
|
|||
import pkgutil
|
||||
import sys
|
||||
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):
|
||||
from typing import GenericMeta
|
||||
|
@ -21,15 +33,35 @@ from . import providers
|
|||
__all__ = (
|
||||
'wire',
|
||||
'unwire',
|
||||
'inject',
|
||||
'Provide',
|
||||
'Provider',
|
||||
'Closing',
|
||||
)
|
||||
|
||||
T = TypeVar('T')
|
||||
F = TypeVar('F', bound=Callable[..., 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:
|
||||
|
||||
def __init__(self, container):
|
||||
|
@ -152,7 +184,7 @@ class ProvidersMap:
|
|||
return providers_map
|
||||
|
||||
|
||||
def wire(
|
||||
def wire( # noqa: C901
|
||||
container: Container,
|
||||
*,
|
||||
modules: Optional[Iterable[ModuleType]] = None,
|
||||
|
@ -179,6 +211,9 @@ def wire(
|
|||
for method_name, method in inspect.getmembers(member, _is_method):
|
||||
_patch_method(member, method_name, method, providers_map)
|
||||
|
||||
for patched in _patched_registry.get_from_module(module):
|
||||
_bind_injections(patched, providers_map)
|
||||
|
||||
|
||||
def unwire(
|
||||
*,
|
||||
|
@ -201,6 +236,17 @@ def unwire(
|
|||
for method_name, method in inspect.getmembers(member, inspect.isfunction):
|
||||
_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(
|
||||
module: ModuleType,
|
||||
|
@ -208,11 +254,16 @@ def _patch_fn(
|
|||
fn: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
) -> None:
|
||||
injections, closing = _resolve_injections(fn, providers_map)
|
||||
if not injections:
|
||||
return
|
||||
patched = _patch_with_injections(fn, injections, closing)
|
||||
setattr(module, name, _wrap_patched(patched, fn, 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)
|
||||
|
||||
setattr(module, name, fn)
|
||||
|
||||
|
||||
def _patch_method(
|
||||
|
@ -221,28 +272,27 @@ def _patch_method(
|
|||
method: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
) -> None:
|
||||
injections, closing = _resolve_injections(method, providers_map)
|
||||
if not injections:
|
||||
return
|
||||
|
||||
if hasattr(cls, '__dict__') \
|
||||
and name in cls.__dict__ \
|
||||
and isinstance(cls.__dict__[name], (classmethod, staticmethod)):
|
||||
method = cls.__dict__[name]
|
||||
patched = _patch_with_injections(method.__func__, injections, closing)
|
||||
patched = type(method)(patched)
|
||||
fn = method.__func__
|
||||
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):
|
||||
patched.__wired__ = True
|
||||
patched.__original__ = original
|
||||
patched.__injections__ = injections
|
||||
patched.__closing__ = closing
|
||||
return patched
|
||||
if isinstance(method, (classmethod, staticmethod)):
|
||||
fn = type(method)(fn)
|
||||
|
||||
setattr(cls, name, fn)
|
||||
|
||||
|
||||
def _unpatch(
|
||||
|
@ -250,14 +300,20 @@ def _unpatch(
|
|||
name: str,
|
||||
fn: Callable[..., Any],
|
||||
) -> 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):
|
||||
return
|
||||
setattr(module, name, _get_original_from_patched(fn))
|
||||
|
||||
_unbind_injections(fn)
|
||||
|
||||
|
||||
def _resolve_injections(
|
||||
def _fetch_reference_injections(
|
||||
fn: Callable[..., Any],
|
||||
providers_map: ProvidersMap,
|
||||
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||
signature = inspect.signature(fn)
|
||||
|
||||
|
@ -268,24 +324,33 @@ def _resolve_injections(
|
|||
continue
|
||||
marker = parameter.default
|
||||
|
||||
closing_modifier = False
|
||||
if isinstance(marker, Closing):
|
||||
closing_modifier = True
|
||||
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)
|
||||
|
||||
if provider is None:
|
||||
continue
|
||||
|
||||
if closing_modifier:
|
||||
closing[parameter_name] = provider
|
||||
|
||||
if isinstance(marker, Provide):
|
||||
injections[parameter_name] = provider
|
||||
fn.__injections__[injection] = 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):
|
||||
|
@ -303,26 +368,34 @@ def _is_method(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):
|
||||
_patched = _get_async_patched(fn, injections, closing)
|
||||
patched = _get_async_patched(fn)
|
||||
else:
|
||||
_patched = _get_patched(fn, injections, closing)
|
||||
return _patched
|
||||
patched = _get_sync_patched(fn)
|
||||
|
||||
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)
|
||||
def _patched(*args, **kwargs):
|
||||
to_inject = kwargs.copy()
|
||||
for injection, provider in injections.items():
|
||||
for injection, provider in _patched.__injections__.items():
|
||||
if injection not in kwargs \
|
||||
or _is_fastapi_default_arg_injection(injection, kwargs):
|
||||
to_inject[injection] = provider()
|
||||
|
||||
result = fn(*args, **to_inject)
|
||||
|
||||
for injection, provider in closing.items():
|
||||
for injection, provider in _patched.__closing__.items():
|
||||
if injection in kwargs \
|
||||
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
||||
continue
|
||||
|
@ -334,18 +407,18 @@ def _get_patched(fn, injections, closing):
|
|||
return _patched
|
||||
|
||||
|
||||
def _get_async_patched(fn, injections, closing):
|
||||
def _get_async_patched(fn):
|
||||
@functools.wraps(fn)
|
||||
async def _patched(*args, **kwargs):
|
||||
to_inject = kwargs.copy()
|
||||
for injection, provider in injections.items():
|
||||
for injection, provider in _patched.__injections__.items():
|
||||
if injection not in kwargs \
|
||||
or _is_fastapi_default_arg_injection(injection, kwargs):
|
||||
to_inject[injection] = provider()
|
||||
|
||||
result = await fn(*args, **to_inject)
|
||||
|
||||
for injection, provider in closing.items():
|
||||
for injection, provider in _patched.__closing__.items():
|
||||
if injection in kwargs \
|
||||
and not _is_fastapi_default_arg_injection(injection, kwargs):
|
||||
continue
|
||||
|
@ -366,10 +439,6 @@ def _is_patched(fn):
|
|||
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:
|
||||
return (not isinstance(instance, type)
|
||||
and getattr(instance, '__IS_CONTAINER__', False) is True
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from decimal import Decimal
|
||||
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 .service import Service
|
||||
|
@ -11,30 +11,37 @@ from .service import Service
|
|||
|
||||
class TestClass:
|
||||
|
||||
@inject
|
||||
def __init__(self, service: Service = Provide[Container.service]):
|
||||
self.service = service
|
||||
|
||||
@inject
|
||||
def method(self, service: Service = Provide[Container.service]):
|
||||
return service
|
||||
|
||||
@classmethod
|
||||
@inject
|
||||
def class_method(cls, service: Service = Provide[Container.service]):
|
||||
return service
|
||||
|
||||
@staticmethod
|
||||
@inject
|
||||
def static_method(service: Service = Provide[Container.service]):
|
||||
return service
|
||||
|
||||
|
||||
@inject
|
||||
def test_function(service: Service = Provide[Container.service]):
|
||||
return service
|
||||
|
||||
|
||||
@inject
|
||||
def test_function_provider(service_provider: Callable[..., Service] = Provider[Container.service]):
|
||||
service = service_provider()
|
||||
return service
|
||||
|
||||
|
||||
@inject
|
||||
def test_config_value(
|
||||
some_value_int: int = Provide[Container.config.a.b.c.as_int()],
|
||||
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
|
||||
|
||||
|
||||
@inject
|
||||
def test_provide_provider(service_provider: Callable[..., Service] = Provider[Container.service.provider]):
|
||||
service = service_provider()
|
||||
return service
|
||||
|
||||
|
||||
@inject
|
||||
def test_provided_instance(some_value: int = Provide[Container.service.provided.foo['bar'].call()]):
|
||||
return some_value
|
||||
|
||||
|
||||
@inject
|
||||
def test_subcontainer_provider(some_value: int = Provide[Container.sub.int_object]):
|
||||
return some_value
|
||||
|
||||
|
||||
@inject
|
||||
def test_config_invariant(some_value: int = Provide[Container.config.option[Container.config.switch]]):
|
||||
return some_value
|
||||
|
||||
|
||||
@inject
|
||||
def test_provide_from_different_containers(
|
||||
service: Service = Provide[Container.service],
|
||||
some_value: int = Provide[SubContainer.int_object],
|
||||
):
|
||||
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 ...service import Service
|
||||
|
||||
|
||||
@inject
|
||||
def test_function(service: Service = Provide[Container.service]):
|
||||
return service
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from dependency_injector import containers, providers
|
||||
from dependency_injector.wiring import Provide, Closing
|
||||
from dependency_injector.wiring import inject, Provide, Closing
|
||||
|
||||
|
||||
class Service:
|
||||
|
@ -32,5 +32,6 @@ class Container(containers.DeclarativeContainer):
|
|||
service = providers.Resource(init_service)
|
||||
|
||||
|
||||
@inject
|
||||
def test_function(service: Service = Closing[Provide[Container.service]]):
|
||||
return service
|
||||
|
|
|
@ -226,6 +226,10 @@ class WiringTest(unittest.TestCase):
|
|||
self.assertEqual(result_2.init_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):
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user