Update examples to use config __init__ args (#527)

* Update application-single-container example

* Update application-multiple-containers example

* Update decoupled-packages example

* Update movie lister example

* Update CLI tutorial

* Update sanic example

* Update sanic example with wiring_config

* Update fastapi example

* Update fastapi-simple example

* Update fastapi-sqlalchemy example

* Update flask-blueprints example

* Update flask example and tutorial

* Update aiohttp example and tutorial

* Update asyncio-daemon example and tutorial
This commit is contained in:
Roman Mogylatov 2021-10-31 20:31:39 -04:00 committed by GitHub
parent 6030950596
commit fe01ad41d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 310 additions and 364 deletions

View File

@ -127,8 +127,6 @@ Now it's time to install the project requirements. We will use next packages:
- ``dependency-injector`` - the dependency injection framework - ``dependency-injector`` - the dependency injection framework
- ``aiohttp`` - the web framework - ``aiohttp`` - the web framework
- ``aiohttp-devtools`` - the helper library that will provide a development server with live
reloading
- ``pyyaml`` - the YAML files parsing library, used for the reading of the configuration files - ``pyyaml`` - the YAML files parsing library, used for the reading of the configuration files
- ``pytest-aiohttp`` - the helper library for the testing of the ``aiohttp`` application - ``pytest-aiohttp`` - the helper library for the testing of the ``aiohttp`` application
- ``pytest-cov`` - the helper library for measuring the test coverage - ``pytest-cov`` - the helper library for measuring the test coverage
@ -139,7 +137,6 @@ Put next lines into the ``requirements.txt`` file:
dependency-injector dependency-injector
aiohttp aiohttp
aiohttp-devtools
pyyaml pyyaml
pytest-aiohttp pytest-aiohttp
pytest-cov pytest-cov
@ -232,26 +229,31 @@ Put next into the ``application.py``:
]) ])
return app return app
if __name__ == "__main__":
app = create_app()
web.run_app(app)
Now we're ready to run our application Now we're ready to run our application
Do next in the terminal: Do next in the terminal:
.. code-block:: bash .. code-block:: bash
adev runserver giphynavigator/application.py --livereload python -m giphynavigator.application
The output should be something like: The output should be something like:
.. code-block:: bash .. code-block:: bash
[18:52:59] Starting aux server at http://localhost:8001 ◆ ======== Running on http://0.0.0.0:8080 ========
[18:52:59] Starting dev server at http://localhost:8000 ● (Press CTRL+C to quit)
Let's check that it works. Open another terminal session and use ``httpie``: Let's check that it works. Open another terminal session and use ``httpie``:
.. code-block:: bash .. code-block:: bash
http http://127.0.0.1:8000/ http http://0.0.0.0:8080/
You should see: You should see:
@ -261,7 +263,7 @@ You should see:
Content-Length: 844 Content-Length: 844
Content-Type: application/json; charset=utf-8 Content-Type: application/json; charset=utf-8
Date: Wed, 29 Jul 2020 21:01:50 GMT Date: Wed, 29 Jul 2020 21:01:50 GMT
Server: Python/3.9 aiohttp/3.6.2 Server: Python/3.10 aiohttp/3.6.2
{ {
"gifs": [], "gifs": [],
@ -328,8 +330,10 @@ Now we need to add ``GiphyClient`` into the container. The ``GiphyClient`` has t
that have to be injected: the API key and the request timeout. We will need to use two more that have to be injected: the API key and the request timeout. We will need to use two more
providers from the ``dependency_injector.providers`` module: providers from the ``dependency_injector.providers`` module:
- ``Factory`` provider that will create the ``GiphyClient`` client. - ``Factory`` provider. It will create a ``GiphyClient`` client.
- ``Configuration`` provider that will provide the API key and the request timeout. - ``Configuration`` provider. It will provide an API key and a request timeout for the ``GiphyClient``
client. We will specify the location of the configuration file. The configuration provider will parse
the configuration file when we create a container instance.
Edit ``containers.py``: Edit ``containers.py``:
@ -345,7 +349,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory( giphy_client = providers.Factory(
giphy.GiphyClient, giphy.GiphyClient,
@ -353,18 +357,8 @@ Edit ``containers.py``:
timeout=config.giphy.request_timeout, timeout=config.giphy.request_timeout,
) )
.. note:: Now let's add the configuration file. We will use YAML. Create an empty file ``config.yml`` in
the root root of the project:
We have used the configuration value before it was defined. That's the principle how the
``Configuration`` provider works.
Use first, define later.
Now let's add the configuration file.
We will use YAML.
Create an empty file ``config.yml`` in the root root of the project:
.. code-block:: bash .. code-block:: bash
:emphasize-lines: 9 :emphasize-lines: 9
@ -387,17 +381,14 @@ and put next into it:
giphy: giphy:
request_timeout: 10 request_timeout: 10
We will use an environment variable ``GIPHY_API_KEY`` to provide the API key.
Now we need to edit ``create_app()`` to make two things when application starts: We will use the ``GIPHY_API_KEY`` environment variable to provide the API key. Lets edit
``create_app()`` to fetch the key value from it.
- Load the configuration file the ``config.yml``.
- Load the API key from the ``GIPHY_API_KEY`` environment variable.
Edit ``application.py``: Edit ``application.py``:
.. code-block:: python .. code-block:: python
:emphasize-lines: 11-12 :emphasize-lines: 11
"""Application module.""" """Application module."""
@ -409,7 +400,6 @@ Edit ``application.py``:
def create_app() -> web.Application: def create_app() -> web.Application:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.giphy.api_key.from_env("GIPHY_API_KEY") container.config.giphy.api_key.from_env("GIPHY_API_KEY")
app = web.Application() app = web.Application()
@ -420,6 +410,10 @@ Edit ``application.py``:
return app return app
if __name__ == "__main__":
app = create_app()
web.run_app(app)
Now we need to create an API key and set it to the environment variable. Now we need to create an API key and set it to the environment variable.
As for now, dont worry, just take this one: As for now, dont worry, just take this one:
@ -502,7 +496,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory( giphy_client = providers.Factory(
giphy.GiphyClient, giphy.GiphyClient,
@ -555,47 +549,50 @@ Edit ``handlers.py``:
}, },
) )
To make the injection work we need to wire the container instance with the ``handlers`` module. To make the injection work we need to wire the container with the ``handlers`` module.
This needs to be done once. After it's done we can use ``Provide`` markers to specify as many Let's configure the container to automatically make wiring with the ``handlers`` module when we
injections as needed for any handler. create a container instance.
Edit ``application.py``: Edit ``containers.py``:
.. code-block:: python .. code-block:: python
:emphasize-lines: 13 :emphasize-lines: 10
"""Application module.""" """Containers module."""
from aiohttp import web from dependency_injector import containers, providers
from .containers import Container from . import giphy, services
from . import handlers
def create_app() -> web.Application: class Container(containers.DeclarativeContainer):
container = Container()
container.config.from_yaml("config.yml")
container.config.giphy.api_key.from_env("GIPHY_API_KEY")
container.wire(modules=[handlers])
app = web.Application() wiring_config = containers.WiringConfiguration(modules=[".handlers"])
app.container = container
app.add_routes([
web.get("/", handlers.index),
])
return app
Make sure the app is running or use: config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory(
giphy.GiphyClient,
api_key=config.giphy.api_key,
timeout=config.giphy.request_timeout,
)
search_service = providers.Factory(
services.SearchService,
giphy_client=giphy_client,
)
Make sure the app is running:
.. code-block:: bash .. code-block:: bash
adev runserver giphynavigator/application.py --livereload python -m giphynavigator.application
and make a request to the API in the terminal: and make a request to the API in the terminal:
.. code-block:: bash .. code-block:: bash
http http://localhost:8000/ query=="wow,it works" limit==5 http http://0.0.0.0:8080/ query=="wow,it works" limit==5
You should see: You should see:
@ -605,7 +602,7 @@ You should see:
Content-Length: 492 Content-Length: 492
Content-Type: application/json; charset=utf-8 Content-Type: application/json; charset=utf-8
Date: Fri, 09 Oct 2020 01:35:48 GMT Date: Fri, 09 Oct 2020 01:35:48 GMT
Server: Python/3.9 aiohttp/3.6.2 Server: Python/3.10 aiohttp/3.6.2
{ {
"gifs": [ "gifs": [
@ -810,24 +807,24 @@ You should see:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.10.0, aiohttp-0.3.0, asyncio-0.14.0 plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
collected 3 items collected 3 items
giphynavigator/tests.py ... [100%] giphynavigator/tests.py ... [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
--------------------------------------------------- ---------------------------------------------------
giphynavigator/__init__.py 0 0 100% giphynavigator/__init__.py 0 0 100%
giphynavigator/application.py 12 0 100% giphynavigator/application.py 13 2 85%
giphynavigator/containers.py 6 0 100% giphynavigator/containers.py 7 0 100%
giphynavigator/giphy.py 14 9 36% giphynavigator/giphy.py 14 9 36%
giphynavigator/handlers.py 10 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 88 10 89% TOTAL 90 12 87%
.. note:: .. note::

View File

@ -135,7 +135,7 @@ Put next lines into the ``Dockerfile`` file:
.. code-block:: bash .. code-block:: bash
FROM python:3.9-buster FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
@ -204,11 +204,11 @@ Logging and configuration
In this section we will configure the logging and configuration file parsing. In this section we will configure the logging and configuration file parsing.
Let's start with the the main part of our application - the container. Container will keep all of Let's start with the the main part of our application the container. Container will keep all of
the application components and their dependencies. the application components and their dependencies.
First two components that we're going to add are the config object and the provider for First two components that we're going to add are the configuration provider and the resource provider
configuring the logging. for configuring the logging.
Put next lines into the ``containers.py`` file: Put next lines into the ``containers.py`` file:
@ -224,7 +224,7 @@ Put next lines into the ``containers.py`` file:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,
@ -233,16 +233,7 @@ Put next lines into the ``containers.py`` file:
format=config.log.format, format=config.log.format,
) )
.. note:: The configuration file will keep the logging settings. Put next lines into the ``config.yml`` file:
We have used the configuration value before it was defined. That's the principle how the
``Configuration`` provider works.
Use first, define later.
The configuration file will keep the logging settings.
Put next lines into the ``config.yml`` file:
.. code-block:: yaml .. code-block:: yaml
@ -250,9 +241,10 @@ Put next lines into the ``config.yml`` file:
level: "INFO" level: "INFO"
format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s" format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s"
Now let's create the function that will run our daemon. It's traditionally called Now let's create the function that will run our daemon. It's traditionally called ``main()``.
``main()``. The ``main()`` function will create the container. Then it will use the container The ``main()`` function will start the dispatcher, but we will keep it empty for now.
to parse the ``config.yml`` file and call the logging configuration provider. We will create the container instance before calling ``main()`` in ``if __name__ == "__main__"``.
Container instance will parse ``config.yml`` and then we will call the logging configuration provider.
Put next lines into the ``__main__.py`` file: Put next lines into the ``__main__.py`` file:
@ -269,7 +261,6 @@ Put next lines into the ``__main__.py`` file:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.init_resources() container.init_resources()
main() main()
@ -419,7 +410,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,
@ -442,7 +433,7 @@ 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-5,9-11,18 :emphasize-lines: 3-5,9-11,17
"""Main module.""" """Main module."""
@ -459,7 +450,6 @@ Edit ``__main__.py``:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.init_resources() container.init_resources()
container.wire(modules=[__name__]) container.wire(modules=[__name__])
@ -559,7 +549,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,
@ -664,7 +654,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,
@ -763,7 +753,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,
@ -888,7 +878,7 @@ Create ``tests.py`` in the ``monitoringdaemon`` package:
and put next into it: and put next into it:
.. code-block:: python .. code-block:: python
:emphasize-lines: 54,70-71 :emphasize-lines: 54,70-73
"""Tests module.""" """Tests module."""
@ -909,8 +899,8 @@ and put next into it:
@pytest.fixture @pytest.fixture
def container(): def container():
container = Container() return Container(
container.config.from_dict({ config={
"log": { "log": {
"level": "INFO", "level": "INFO",
"formant": "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s", "formant": "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s",
@ -929,8 +919,8 @@ and put next into it:
"check_every": 1, "check_every": 1,
}, },
}, },
}) }
return container )
@pytest.mark.asyncio @pytest.mark.asyncio
@ -959,9 +949,10 @@ and put next into it:
example_monitor_mock = mock.AsyncMock() example_monitor_mock = mock.AsyncMock()
httpbin_monitor_mock = mock.AsyncMock() httpbin_monitor_mock = mock.AsyncMock()
with container.example_monitor.override(example_monitor_mock), \ with container.override_providers(
container.httpbin_monitor.override(httpbin_monitor_mock): example_monitor=example_monitor_mock,
httpbin_monitor=httpbin_monitor_mock,
):
dispatcher = container.dispatcher() dispatcher = container.dispatcher()
event_loop.create_task(dispatcher.start()) event_loop.create_task(dispatcher.start())
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -980,25 +971,25 @@ You should see:
.. code-block:: bash .. code-block:: bash
platform linux -- Python 3.9, pytest-6.0.1, py-1.9.0, pluggy-0.13.1 platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code rootdir: /code
plugins: asyncio-0.14.0, cov-2.10.0 plugins: asyncio-0.16.0, cov-3.0.0
collected 2 items collected 2 items
monitoringdaemon/tests.py .. [100%] monitoringdaemon/tests.py .. [100%]
----------- coverage: platform linux, python 3.9 ----------- ---------- coverage: platform linux, python 3.10.0-final-0 -----------
Name Stmts Miss Cover Name Stmts Miss Cover
---------------------------------------------------- ----------------------------------------------------
monitoringdaemon/__init__.py 0 0 100% monitoringdaemon/__init__.py 0 0 100%
monitoringdaemon/__main__.py 13 13 0% monitoringdaemon/__main__.py 11 11 0%
monitoringdaemon/containers.py 11 0 100% monitoringdaemon/containers.py 11 0 100%
monitoringdaemon/dispatcher.py 44 5 89% monitoringdaemon/dispatcher.py 45 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 35 0 100%
---------------------------------------------------- ----------------------------------------------------
TOTAL 134 22 84% TOTAL 131 20 85%
.. note:: .. note::

View File

@ -428,7 +428,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
movie = providers.Factory(entities.Movie) movie = providers.Factory(entities.Movie)
@ -445,15 +445,9 @@ This is also called the delegation of the provider. If we just pass the movie fa
as the dependency, it will be called when csv finder is created and the ``Movie`` instance will as the dependency, it will be called when csv finder is created and the ``Movie`` instance will
be injected. With the ``.provider`` attribute the provider itself will be injected. be injected. With the ``.provider`` attribute the provider itself will be injected.
The csv finder also has a few dependencies on the configuration options. We added configuration The csv finder also has a few dependencies on the configuration options. We added a configuration
provider to provide these dependencies. provider to provide these dependencies and specified the location of the configuration file.
The configuration provider will parse the configuration file when we create a container instance.
.. note::
We have used the configuration value before it was defined. That's the principle how the
Configuration provider works.
Use first, define later.
Not let's define the configuration values. Not let's define the configuration values.
@ -467,29 +461,7 @@ Edit ``config.yml``:
path: "data/movies.csv" path: "data/movies.csv"
delimiter: "," delimiter: ","
The configuration file is ready. Now let's update the ``main()`` function to specify its location. The configuration file is ready. Move on to the lister.
Edit ``__main__.py``:
.. code-block:: python
:emphasize-lines: 12
"""Main module."""
from .containers import Container
def main() -> None:
...
if __name__ == "__main__":
container = Container()
container.config.from_yaml("config.yml")
main()
Move on to the lister.
Create the ``listers.py`` in the ``movies`` package: Create the ``listers.py`` in the ``movies`` package:
@ -552,7 +524,7 @@ and edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
movie = providers.Factory(entities.Movie) movie = providers.Factory(entities.Movie)
@ -575,7 +547,7 @@ Let's inject the ``lister`` into the ``main()`` function.
Edit ``__main__.py``: Edit ``__main__.py``:
.. code-block:: python .. code-block:: python
:emphasize-lines: 3-5,9-10,17 :emphasize-lines: 3-5,9-10,16
"""Main module.""" """Main module."""
@ -592,7 +564,6 @@ Edit ``__main__.py``:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.wire(modules=[__name__]) container.wire(modules=[__name__])
main() main()
@ -628,7 +599,6 @@ Edit ``__main__.py``:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.wire(modules=[__name__]) container.wire(modules=[__name__])
main() main()
@ -733,7 +703,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
movie = providers.Factory(entities.Movie) movie = providers.Factory(entities.Movie)
@ -822,7 +792,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
movie = providers.Factory(entities.Movie) movie = providers.Factory(entities.Movie)
@ -859,7 +829,7 @@ 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: 23 :emphasize-lines: 22
"""Main module.""" """Main module."""
@ -882,7 +852,6 @@ Edit ``__main__.py``:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.finder.type.from_env("MOVIE_FINDER_TYPE") container.config.finder.type.from_env("MOVIE_FINDER_TYPE")
container.wire(modules=[sys.modules[__name__]]) container.wire(modules=[sys.modules[__name__]])
@ -942,7 +911,7 @@ Create ``tests.py`` in the ``movies`` package:
and put next into it: and put next into it:
.. code-block:: python .. code-block:: python
:emphasize-lines: 35,50 :emphasize-lines: 36,51
"""Tests module.""" """Tests module."""
@ -955,8 +924,8 @@ and put next into it:
@pytest.fixture @pytest.fixture
def container(): def container():
container = Container() container = Container(
container.config.from_dict({ config={
"finder": { "finder": {
"type": "csv", "type": "csv",
"csv": { "csv": {
@ -967,7 +936,8 @@ and put next into it:
"path": "/fake-movies.db", "path": "/fake-movies.db",
}, },
}, },
}) },
)
return container return container
@ -1010,24 +980,24 @@ You should see:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.10.0 plugins: cov-3.0.0
collected 2 items collected 2 items
movies/tests.py .. [100%] movies/tests.py .. [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10 -----------
Name Stmts Miss Cover Name Stmts Miss Cover
------------------------------------------ ------------------------------------------
movies/__init__.py 0 0 100% movies/__init__.py 0 0 100%
movies/__main__.py 18 18 0% movies/__main__.py 16 16 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 23 0 100%
------------------------------------------ ------------------------------------------
TOTAL 92 32 65% TOTAL 89 30 66%
.. note:: .. note::
@ -1047,7 +1017,7 @@ We've used the ``Dependency Injector`` as a dependency injection framework.
With a help of :ref:`containers` and :ref:`providers` we have defined how to assemble application components. With a help of :ref:`containers` and :ref:`providers` we have defined how to assemble application components.
``Selector`` provider served as a switch for selecting the database format based on a configuration. ``Selector`` provider served as a switch for selecting the database format based on a configuration.
:ref:`configuration-provider` helped to deal with reading YAML file and environment variable. :ref:`configuration-provider` helped to deal with reading a YAML file and environment variables.
We used :ref:`wiring` feature to inject the dependencies into the ``main()`` function. We used :ref:`wiring` feature to inject the dependencies into the ``main()`` function.
:ref:`provider-overriding` feature helped in testing. :ref:`provider-overriding` feature helped in testing.

View File

@ -112,7 +112,7 @@ You should see something like:
(venv) $ python -c "import dependency_injector; print(dependency_injector.__version__)" (venv) $ python -c "import dependency_injector; print(dependency_injector.__version__)"
4.37.0 4.37.0
(venv) $ python -c "import flask; print(flask.__version__)" (venv) $ python -c "import flask; print(flask.__version__)"
1.1.2 2.0.2
*Versions can be different. That's fine.* *Versions can be different. That's fine.*
@ -444,9 +444,10 @@ and run in the terminal:
Now we need to add Github API client the container. We will need to add two more providers from Now we need to add Github API client the container. We will need to add two more providers from
the ``dependency_injector.providers`` module: the ``dependency_injector.providers`` module:
- ``Factory`` provider that will create ``Github`` client. - ``Factory`` provider. It will create a ``Github`` client.
- ``Configuration`` provider that will be used for providing the API token and the request timeout - ``Configuration`` provider. It will provide an API token and a request timeout for the ``Github`` client.
for the ``Github`` client. We will specify the location of the configuration file. The configuration provider will parse
the configuration file when we create a container instance.
Edit ``containers.py``: Edit ``containers.py``:
@ -461,7 +462,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
github_client = providers.Factory( github_client = providers.Factory(
Github, Github,
@ -469,23 +470,14 @@ Edit ``containers.py``:
timeout=config.github.request_timeout, timeout=config.github.request_timeout,
) )
.. note::
We have used the configuration value before it was defined. That's the principle how
``Configuration`` provider works.
Use first, define later.
.. note:: .. note::
Don't forget to remove the Ellipsis ``...`` from the container. We don't need it anymore Don't forget to remove the Ellipsis ``...`` from the container. We don't need it anymore
since we container is not empty. since we container is not empty.
Now let's add the configuration file. Now let's add the configuration file. We will use YAML. Create an empty file ``config.yml``
in the root of the project:
We will use YAML.
Create an empty file ``config.yml`` in the root of the project:
.. code-block:: bash .. code-block:: bash
:emphasize-lines: 11 :emphasize-lines: 11
@ -530,17 +522,13 @@ and install it:
pip install -r requirements.txt pip install -r requirements.txt
We will use environment variable ``GITHUB_TOKEN`` to provide the API token. We will use the ``GITHUB_TOKEN`` environment variable to provide the API token. Let's edit
``create_app()`` to fetch the token value from it.
Now we need to edit ``create_app()`` to make two things when application starts:
- Load the configuration file the ``config.yml``.
- Load the API token from the ``GITHUB_TOKEN`` environment variable.
Edit ``application.py``: Edit ``application.py``:
.. code-block:: python .. code-block:: python
:emphasize-lines: 12-13 :emphasize-lines: 12
"""Application module.""" """Application module."""
@ -553,7 +541,6 @@ Edit ``application.py``:
def create_app() -> Flask: def create_app() -> Flask:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.github.auth_token.from_env("GITHUB_TOKEN") container.config.github.auth_token.from_env("GITHUB_TOKEN")
app = Flask(__name__) app = Flask(__name__)
@ -684,7 +671,7 @@ Edit ``containers.py``:
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
github_client = providers.Factory( github_client = providers.Factory(
Github, Github,
@ -732,38 +719,39 @@ Edit ``views.py``:
repositories=repositories, repositories=repositories,
) )
To make the injection work we need to wire the container instance with the ``views`` module. To make the injection work we need to wire the container with the ``views`` module.
This needs to be done once. After it's done we can use ``Provide`` markers to specify as many Let's configure the container to automatically make wiring with the ``views`` module when we
injections as needed for any view. create a container instance.
Edit ``application.py``: Edit ``containers.py``:
.. code-block:: python .. code-block:: python
:emphasize-lines: 14 :emphasize-lines: 11
"""Application module.""" """Containers module."""
from flask import Flask from dependency_injector import containers, providers
from flask_bootstrap import Bootstrap from github import Github
from .containers import Container from . import services
from . import views
def create_app() -> Flask: class Container(containers.DeclarativeContainer):
container = Container()
container.config.from_yaml("config.yml")
container.config.github.auth_token.from_env("GITHUB_TOKEN")
container.wire(modules=[views])
app = Flask(__name__) wiring_config = containers.WiringConfiguration(modules=[".views"])
app.container = container
app.add_url_rule("/", "index", views.index)
bootstrap = Bootstrap() config = providers.Configuration(yaml_files=["config.yml"])
bootstrap.init_app(app)
return app github_client = providers.Factory(
Github,
login_or_token=config.github.auth_token,
timeout=config.github.request_timeout,
)
search_service = providers.Factory(
services.SearchService,
github_client=github_client,
)
Make sure the app is running or use ``flask run`` and open ``http://127.0.0.1:5000/``. Make sure the app is running or use ``flask run`` and open ``http://127.0.0.1:5000/``.
@ -960,23 +948,23 @@ You should see:
.. code-block:: bash .. code-block:: bash
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: flask-1.0.0, cov-2.10.0 plugins: cov-3.0.0, flask-1.2.0
collected 2 items collected 2 items
githubnavigator/tests.py .. [100%] githubnavigator/tests.py .. [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
---------------------------------------------------- ----------------------------------------------------
githubnavigator/__init__.py 0 0 100% githubnavigator/__init__.py 0 0 100%
githubnavigator/application.py 15 0 100% githubnavigator/application.py 13 0 100%
githubnavigator/containers.py 7 0 100% githubnavigator/containers.py 8 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 10 0 100% githubnavigator/views.py 10 0 100%
---------------------------------------------------- ----------------------------------------------------
TOTAL 80 0 100% TOTAL 79 0 100%
.. note:: .. note::

View File

@ -27,16 +27,16 @@ To run the application do:
.. code-block:: bash .. code-block:: bash
export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0 export GIPHY_API_KEY=wBJ2wZG7SRqfrU9nPgPiWvORmloDyuL0
adev runserver giphynavigator/application.py --livereload python -m giphynavigator.application
The output should be something like: The output should be something like:
.. code-block:: .. code-block::
[18:52:59] Starting aux server at http://localhost:8001 ◆ ======== Running on http://0.0.0.0:8080 ========
[18:52:59] Starting dev server at http://localhost:8000 ● (Press CTRL+C to quit)
After that visit http://127.0.0.1:8000/ in your browser or use CLI command (``curl``, ``httpie``, After that visit http://0.0.0.0:8080/ in your browser or use CLI command (``curl``, ``httpie``,
etc). You should see something like: etc). You should see something like:
.. code-block:: json .. code-block:: json
@ -98,21 +98,21 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.10.0, aiohttp-0.3.0, asyncio-0.14.0 plugins: asyncio-0.16.0, anyio-3.3.4, aiohttp-0.3.0, cov-3.0.0
collected 3 items collected 3 items
giphynavigator/tests.py ... [100%] giphynavigator/tests.py ... [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
--------------------------------------------------- ---------------------------------------------------
giphynavigator/__init__.py 0 0 100% giphynavigator/__init__.py 0 0 100%
giphynavigator/application.py 12 0 100% giphynavigator/application.py 13 2 85%
giphynavigator/containers.py 6 0 100% giphynavigator/containers.py 7 0 100%
giphynavigator/giphy.py 14 9 36% giphynavigator/giphy.py 14 9 36%
giphynavigator/handlers.py 10 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 88 10 89% TOTAL 90 12 87%

View File

@ -8,9 +8,7 @@ from . import handlers
def create_app() -> web.Application: def create_app() -> web.Application:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.giphy.api_key.from_env("GIPHY_API_KEY") container.config.giphy.api_key.from_env("GIPHY_API_KEY")
container.wire(modules=[handlers])
app = web.Application() app = web.Application()
app.container = container app.container = container
@ -18,3 +16,8 @@ def create_app() -> web.Application:
web.get("/", handlers.index), web.get("/", handlers.index),
]) ])
return app return app
if __name__ == "__main__":
app = create_app()
web.run_app(app)

View File

@ -7,7 +7,9 @@ from . import giphy, services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(modules=[".handlers"])
config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory( giphy_client = providers.Factory(
giphy.GiphyClient, giphy.GiphyClient,

View File

@ -1,6 +1,5 @@
dependency-injector dependency-injector
aiohttp aiohttp
aiohttp-devtools
pyyaml pyyaml
pytest-aiohttp pytest-aiohttp
pytest-cov pytest-cov

View File

@ -24,7 +24,6 @@ def main(
if __name__ == "__main__": if __name__ == "__main__":
application = Application() application = Application()
application.config.from_yaml("config.yml")
application.core.init_resources() application.core.init_resources()
application.wire(modules=[__name__]) application.wire(modules=[__name__])

View File

@ -61,7 +61,7 @@ class Services(containers.DeclarativeContainer):
class Application(containers.DeclarativeContainer): class Application(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
core = providers.Container( core = providers.Container(
Core, Core,

View File

@ -25,7 +25,6 @@ def main(
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.init_resources() container.init_resources()
container.config.from_ini("config.ini")
container.wire(modules=[__name__]) container.wire(modules=[__name__])
main(*sys.argv[1:]) main(*sys.argv[1:])

View File

@ -11,7 +11,7 @@ from . import services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(ini_files=["config.ini"])
logging = providers.Resource( logging = providers.Resource(
logging.config.fileConfig, logging.config.fileConfig,

View File

@ -1,4 +1,4 @@
FROM python:3.9-buster FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1

View File

@ -65,22 +65,22 @@ The output should be something like:
.. code-block:: .. code-block::
platform linux -- Python 3.9, pytest-6.0.1, py-1.9.0, pluggy-0.13.1 platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code rootdir: /code
plugins: asyncio-0.14.0, cov-2.10.0 plugins: asyncio-0.16.0, cov-3.0.0
collected 2 items collected 2 items
monitoringdaemon/tests.py .. [100%] monitoringdaemon/tests.py .. [100%]
----------- coverage: platform linux, python 3.9 ----------- ---------- coverage: platform linux, python 3.10.0-final-0 -----------
Name Stmts Miss Cover Name Stmts Miss Cover
---------------------------------------------------- ----------------------------------------------------
monitoringdaemon/__init__.py 0 0 100% monitoringdaemon/__init__.py 0 0 100%
monitoringdaemon/__main__.py 13 13 0% monitoringdaemon/__main__.py 11 11 0%
monitoringdaemon/containers.py 11 0 100% monitoringdaemon/containers.py 11 0 100%
monitoringdaemon/dispatcher.py 44 5 89% monitoringdaemon/dispatcher.py 45 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 35 0 100%
---------------------------------------------------- ----------------------------------------------------
TOTAL 134 22 84% TOTAL 131 20 85%

View File

@ -13,7 +13,6 @@ def main(dispatcher: Dispatcher = Provide[Container.dispatcher]) -> None:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.init_resources() container.init_resources()
container.wire(modules=[__name__]) container.wire(modules=[__name__])

View File

@ -10,7 +10,7 @@ from . import http, monitors, dispatcher
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
logging = providers.Resource( logging = providers.Resource(
logging.basicConfig, logging.basicConfig,

View File

@ -17,8 +17,8 @@ class RequestStub:
@pytest.fixture @pytest.fixture
def container(): def container():
container = Container() return Container(
container.config.from_dict({ config={
"log": { "log": {
"level": "INFO", "level": "INFO",
"formant": "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s", "formant": "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s",
@ -37,8 +37,8 @@ def container():
"check_every": 1, "check_every": 1,
}, },
}, },
}) }
return container )
@pytest.mark.asyncio @pytest.mark.asyncio
@ -67,9 +67,10 @@ async def test_dispatcher(container, caplog, event_loop):
example_monitor_mock = mock.AsyncMock() example_monitor_mock = mock.AsyncMock()
httpbin_monitor_mock = mock.AsyncMock() httpbin_monitor_mock = mock.AsyncMock()
with container.example_monitor.override(example_monitor_mock), \ with container.override_providers(
container.httpbin_monitor.override(httpbin_monitor_mock): example_monitor=example_monitor_mock,
httpbin_monitor=httpbin_monitor_mock,
):
dispatcher = container.dispatcher() dispatcher = container.dispatcher()
event_loop.create_task(dispatcher.start()) event_loop.create_task(dispatcher.start())
await asyncio.sleep(0.1) await asyncio.sleep(0.1)

View File

@ -35,7 +35,6 @@ def main(
if __name__ == "__main__": if __name__ == "__main__":
application = ApplicationContainer() application = ApplicationContainer()
application.config.from_ini("config.ini")
application.wire(modules=[__name__]) application.wire(modules=[__name__])
main() main()

View File

@ -12,7 +12,7 @@ from .analytics.containers import AnalyticsContainer
class ApplicationContainer(containers.DeclarativeContainer): class ApplicationContainer(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(ini_files=["config.ini"])
sqlite = providers.Singleton(sqlite3.connect, config.database.dsn) sqlite = providers.Singleton(sqlite3.connect, config.database.dsn)

View File

@ -7,10 +7,9 @@ from fastapi_di_example import app, container, Service
@pytest.fixture @pytest.fixture
def client(event_loop): async def client(event_loop):
client = AsyncClient(app=app, base_url="http://test") async with AsyncClient(app=app, base_url="http://test") as client:
yield client yield client
event_loop.run_until_complete(client.aclose())
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@ -1,4 +1,4 @@
FROM python:3.9-buster FROM python:3.10-buster
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0

View File

@ -73,19 +73,19 @@ The output should be something like:
.. code-block:: .. code-block::
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 platform linux -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /code rootdir: /code
plugins: cov-2.11.1 plugins: cov-3.0.0
collected 7 items collected 7 items
webapp/tests.py ....... [100%] webapp/tests.py ....... [100%]
----------- coverage: platform linux, python 3.9.1-final-0 ----------- ---------- coverage: platform linux, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
-------------------------------------------- --------------------------------------------
webapp/__init__.py 0 0 100% webapp/__init__.py 0 0 100%
webapp/application.py 14 0 100% webapp/application.py 12 0 100%
webapp/containers.py 9 0 100% webapp/containers.py 10 0 100%
webapp/database.py 24 8 67% webapp/database.py 24 8 67%
webapp/endpoints.py 32 0 100% webapp/endpoints.py 32 0 100%
webapp/models.py 10 1 90% webapp/models.py 10 1 90%
@ -93,4 +93,4 @@ The output should be something like:
webapp/services.py 16 0 100% webapp/services.py 16 0 100%
webapp/tests.py 59 0 100% webapp/tests.py 59 0 100%
-------------------------------------------- --------------------------------------------
TOTAL 200 29 86% TOTAL 199 29 85%

View File

@ -8,8 +8,6 @@ from . import endpoints
def create_app() -> FastAPI: def create_app() -> FastAPI:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.wire(modules=[endpoints])
db = container.db() db = container.db()
db.create_database() db.create_database()

View File

@ -9,7 +9,9 @@ from .services import UserService
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(modules=[".endpoints"])
config = providers.Configuration(yaml_files=["config.yml"])
db = providers.Singleton(Database, db_url=config.db.url) db = providers.Singleton(Database, db_url=config.db.url)

View File

@ -101,21 +101,21 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.10.0, asyncio-0.14.0 plugins: asyncio-0.16.0, cov-3.0.0
collected 3 items collected 3 items
giphynavigator/tests.py ... [100%] giphynavigator/tests.py ... [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
--------------------------------------------------- ---------------------------------------------------
giphynavigator/__init__.py 0 0 100% giphynavigator/__init__.py 0 0 100%
giphynavigator/application.py 13 0 100% giphynavigator/application.py 11 0 100%
giphynavigator/containers.py 6 0 100% giphynavigator/containers.py 7 0 100%
giphynavigator/endpoints.py 20 0 100% giphynavigator/endpoints.py 20 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 37 0 100%
--------------------------------------------------- ---------------------------------------------------
TOTAL 100 10 90% TOTAL 98 10 90%

View File

@ -8,9 +8,7 @@ from . import endpoints
def create_app() -> FastAPI: def create_app() -> FastAPI:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.giphy.api_key.from_env("GIPHY_API_KEY") container.config.giphy.api_key.from_env("GIPHY_API_KEY")
container.wire(modules=[endpoints])
app = FastAPI() app = FastAPI()
app.container = container app.container = container

View File

@ -7,7 +7,9 @@ from . import giphy, services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(modules=[".endpoints"])
config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory( giphy_client = providers.Factory(
giphy.GiphyClient, giphy.GiphyClient,

View File

@ -10,10 +10,9 @@ from giphynavigator.giphy import GiphyClient
@pytest.fixture @pytest.fixture
def client(event_loop): async def client():
client = AsyncClient(app=app, base_url="http://test") async with AsyncClient(app=app, base_url="http://test") as client:
yield client yield client
event_loop.run_until_complete(client.aclose())
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@ -81,20 +81,21 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: flask-1.0.0, cov-2.10.0 plugins: cov-3.0.0, flask-1.2.0
collected 2 items collected 2 items
githubnavigator/tests.py .. [100%] githubnavigator/tests.py .. [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
---------------------------------------------------- ------------------------------------------------------------
githubnavigator/__init__.py 0 0 100% githubnavigator/__init__.py 0 0 100%
githubnavigator/application.py 15 0 100% githubnavigator/application.py 13 0 100%
githubnavigator/blueprints/__init__.py 0 0 100%
githubnavigator/blueprints/example.py 12 0 100% githubnavigator/blueprints/example.py 12 0 100%
githubnavigator/containers.py 7 0 100% githubnavigator/containers.py 8 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%
----------------------------------------------------------- ------------------------------------------------------------
TOTAL 82 0 100% TOTAL 81 0 100%

View File

@ -9,9 +9,7 @@ from .blueprints import example
def create_app() -> Flask: def create_app() -> Flask:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.github.auth_token.from_env("GITHUB_TOKEN") container.config.github.auth_token.from_env("GITHUB_TOKEN")
container.wire(modules=[example])
app = Flask(__name__) app = Flask(__name__)
app.container = container app.container = container

View File

@ -8,7 +8,9 @@ from . import services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(packages=[".blueprints"])
config = providers.Configuration(yaml_files=["config.yml"])
github_client = providers.Factory( github_client = providers.Factory(
Github, Github,

View File

@ -81,20 +81,20 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: flask-1.0.0, cov-2.10.0 plugins: cov-3.0.0, flask-1.2.0
collected 2 items collected 2 items
githubnavigator/tests.py .. [100%] githubnavigator/tests.py .. [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
---------------------------------------------------- ----------------------------------------------------
githubnavigator/__init__.py 0 0 100% githubnavigator/__init__.py 0 0 100%
githubnavigator/application.py 15 0 100% githubnavigator/application.py 13 0 100%
githubnavigator/containers.py 7 0 100% githubnavigator/containers.py 8 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 10 0 100% githubnavigator/views.py 10 0 100%
---------------------------------------------------- ----------------------------------------------------
TOTAL 80 0 100% TOTAL 79 0 100%

View File

@ -9,9 +9,7 @@ from . import views
def create_app() -> Flask: def create_app() -> Flask:
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.github.auth_token.from_env("GITHUB_TOKEN") container.config.github.auth_token.from_env("GITHUB_TOKEN")
container.wire(modules=[views])
app = Flask(__name__) app = Flask(__name__)
app.container = container app.container = container

View File

@ -8,7 +8,9 @@ from . import services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(modules=[".views"])
config = providers.Configuration(yaml_files=["config.yml"])
github_client = providers.Factory( github_client = providers.Factory(
Github, Github,

View File

@ -58,21 +58,21 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-5.4.3, py-1.9.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.10.0 plugins: cov-3.0.0
collected 2 items collected 2 items
movies/tests.py .. [100%] movies/tests.py .. [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10 -----------
Name Stmts Miss Cover Name Stmts Miss Cover
------------------------------------------ ------------------------------------------
movies/__init__.py 0 0 100% movies/__init__.py 0 0 100%
movies/__main__.py 18 18 0% movies/__main__.py 16 16 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 23 0 100%
------------------------------------------ ------------------------------------------
TOTAL 92 32 65% TOTAL 89 30 66%

View File

@ -19,7 +19,6 @@ def main(lister: MovieLister = Provide[Container.lister]) -> None:
if __name__ == "__main__": if __name__ == "__main__":
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.finder.type.from_env("MOVIE_FINDER_TYPE") container.config.finder.type.from_env("MOVIE_FINDER_TYPE")
container.wire(modules=[__name__]) container.wire(modules=[__name__])

View File

@ -7,7 +7,7 @@ from . import finders, listers, entities
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() config = providers.Configuration(yaml_files=["config.yml"])
movie = providers.Factory(entities.Movie) movie = providers.Factory(entities.Movie)

View File

@ -9,8 +9,8 @@ from .containers import Container
@pytest.fixture @pytest.fixture
def container(): def container():
container = Container() container = Container(
container.config.from_dict({ config={
"finder": { "finder": {
"type": "csv", "type": "csv",
"csv": { "csv": {
@ -21,7 +21,8 @@ def container():
"path": "/fake-movies.db", "path": "/fake-movies.db",
}, },
}, },
}) },
)
return container return container

View File

@ -98,22 +98,22 @@ The output should be something like:
.. code-block:: .. code-block::
platform darwin -- Python 3.9, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 platform darwin -- Python 3.10.0, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
plugins: cov-2.12.1, sanic-1.8.1, anyio-3.3.2 plugins: sanic-1.9.1, anyio-3.3.4, cov-3.0.0
collected 3 items collected 3 items
giphynavigator/tests.py ... [100%] giphynavigator/tests.py ... [100%]
---------- coverage: platform darwin, python 3.9 ----------- ---------- coverage: platform darwin, python 3.10.0-final-0 ----------
Name Stmts Miss Cover Name Stmts Miss Cover
--------------------------------------------------- ---------------------------------------------------
giphynavigator/__init__.py 0 0 100% giphynavigator/__init__.py 0 0 100%
giphynavigator/__main__.py 4 4 0% giphynavigator/__main__.py 4 4 0%
giphynavigator/application.py 12 0 100% giphynavigator/application.py 10 0 100%
giphynavigator/containers.py 6 0 100% giphynavigator/containers.py 7 0 100%
giphynavigator/giphy.py 14 9 36% giphynavigator/giphy.py 14 9 36%
giphynavigator/handlers.py 11 0 100% giphynavigator/handlers.py 11 0 100%
giphynavigator/services.py 9 1 89% giphynavigator/services.py 9 1 89%
giphynavigator/tests.py 39 0 100% giphynavigator/tests.py 39 0 100%
--------------------------------------------------- ---------------------------------------------------
TOTAL 95 14 85% TOTAL 94 14 85%

View File

@ -9,9 +9,7 @@ from . import handlers
def create_app() -> Sanic: def create_app() -> Sanic:
"""Create and return Sanic application.""" """Create and return Sanic application."""
container = Container() container = Container()
container.config.from_yaml("config.yml")
container.config.giphy.api_key.from_env("GIPHY_API_KEY") container.config.giphy.api_key.from_env("GIPHY_API_KEY")
container.wire(modules=[handlers])
app = Sanic("giphy-navigator") app = Sanic("giphy-navigator")
app.ctx.container = container app.ctx.container = container

View File

@ -7,7 +7,9 @@ from . import giphy, services
class Container(containers.DeclarativeContainer): class Container(containers.DeclarativeContainer):
config = providers.Configuration() wiring_config = containers.WiringConfiguration(modules=[".handlers"])
config = providers.Configuration(yaml_files=["config.yml"])
giphy_client = providers.Factory( giphy_client = providers.Factory(
giphy.GiphyClient, giphy.GiphyClient,

View File

@ -1,5 +1,5 @@
dependency-injector dependency-injector
sanic sanic<=21.6
aiohttp aiohttp
pyyaml pyyaml
pytest-sanic pytest-sanic