mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 09:57:37 +03:00 
			
		
		
		
	Merge branch 'release/4.4.0' into master
This commit is contained in:
		
						commit
						262c035bc1
					
				
							
								
								
									
										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/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										89
									
								
								docs/examples/flask-blueprints.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								docs/examples/flask-blueprints.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
.. _flask-blueprints-example:
 | 
			
		||||
 | 
			
		||||
Flask blueprints example
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
.. meta::
 | 
			
		||||
   :keywords: Python,Dependency Injection,Flask,Blueprints,Example
 | 
			
		||||
   :description: This example demonstrates a usage of the Flask Blueprints and Dependency Injector.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
This example shows how to use ``Dependency Injector`` with `Flask <https://flask.palletsprojects.com/en/1.1.x/>`_
 | 
			
		||||
blueprints.
 | 
			
		||||
 | 
			
		||||
The example application helps to search for repositories on the Github.
 | 
			
		||||
 | 
			
		||||
.. image:: images/flask.png
 | 
			
		||||
    :width: 100%
 | 
			
		||||
    :align: center
 | 
			
		||||
 | 
			
		||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
 | 
			
		||||
 | 
			
		||||
Application structure
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
Application has next structure:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   ./
 | 
			
		||||
   ├── githubnavigator/
 | 
			
		||||
   │   ├── blueprints
 | 
			
		||||
   │   │   ├── __init__.py
 | 
			
		||||
   │   │   └── example.py
 | 
			
		||||
   │   ├── templates
 | 
			
		||||
   │   │   ├── base.html
 | 
			
		||||
   │   │   └── index.py
 | 
			
		||||
   │   ├── __init__.py
 | 
			
		||||
   │   ├── application.py
 | 
			
		||||
   │   ├── containers.py
 | 
			
		||||
   │   ├── services.py
 | 
			
		||||
   │   └── tests.py
 | 
			
		||||
   ├── config.yml
 | 
			
		||||
   └── requirements.txt
 | 
			
		||||
 | 
			
		||||
Container
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
Declarative container is defined in ``githubnavigator/containers.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/containers.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Blueprints
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
Blueprint's view has dependencies on search service and some config options. The dependencies are injected
 | 
			
		||||
using :ref:`wiring` feature.
 | 
			
		||||
 | 
			
		||||
Listing of ``githubnavigator/blueprints/example.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/blueprints/example.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Application factory
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
Application factory creates container, wires it with the blueprints, creates
 | 
			
		||||
``Flask`` app, and setup routes.
 | 
			
		||||
 | 
			
		||||
Listing of ``githubnavigator/application.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/application.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Tests
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Tests use :ref:`provider-overriding` feature to replace github client with a mock ``githubnavigator/tests.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/flask-blueprints/githubnavigator/tests.py
 | 
			
		||||
   :language: python
 | 
			
		||||
   :emphasize-lines: 44,67
 | 
			
		||||
 | 
			
		||||
Sources
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Explore the sources on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/flask-blueprints>`_.
 | 
			
		||||
 | 
			
		||||
.. disqus::
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ Explore the examples to see the ``Dependency Injector`` in action.
 | 
			
		|||
    decoupled-packages
 | 
			
		||||
    django
 | 
			
		||||
    flask
 | 
			
		||||
    flask-blueprints
 | 
			
		||||
    aiohttp
 | 
			
		||||
    sanic
 | 
			
		||||
    fastapi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +283,7 @@ Choose one of the following as a next step:
 | 
			
		|||
    - :ref:`decoupled-packages`
 | 
			
		||||
    - :ref:`django-example`
 | 
			
		||||
    - :ref:`flask-example`
 | 
			
		||||
    - :ref:`flask-blueprints-example`
 | 
			
		||||
    - :ref:`aiohttp-example`
 | 
			
		||||
    - :ref:`sanic-example`
 | 
			
		||||
    - :ref:`fastapi-example`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,17 @@ that were made in every particular version.
 | 
			
		|||
From version 0.7.6 *Dependency Injector* framework strictly 
 | 
			
		||||
follows `Semantic versioning`_
 | 
			
		||||
 | 
			
		||||
4.4.0
 | 
			
		||||
-----
 | 
			
		||||
- Add ``@inject`` decorator. It helps to fix a number of wiring bugs and make wiring be more resilient.
 | 
			
		||||
- Refactor ``wiring`` module.
 | 
			
		||||
- Update documentation and examples to use ``@inject`` decorator.
 | 
			
		||||
- Add ``Flask`` blueprints example.
 | 
			
		||||
- Fix wiring bug when wiring doesn't work with the class-based decorators.
 | 
			
		||||
- Fix wiring bug when wiring doesn't work with the decorators that doesn't use ``functools.wraps(...)``.
 | 
			
		||||
- Fix wiring bug with ``@app.route(...)`` -style decorators (Flask, Sanic, FastAPI, etc.).
 | 
			
		||||
- Fix wiring bug when wiring doesn't work with Flask blueprints.
 | 
			
		||||
 | 
			
		||||
4.3.9
 | 
			
		||||
-----
 | 
			
		||||
- Add ``FastAPI`` example.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -199,6 +207,7 @@ Take a look at other application examples:
 | 
			
		|||
- :ref:`decoupled-packages`
 | 
			
		||||
- :ref:`django-example`
 | 
			
		||||
- :ref:`flask-example`
 | 
			
		||||
- :ref:`flask-blueprints-example`
 | 
			
		||||
- :ref:`aiohttp-example`
 | 
			
		||||
- :ref:`sanic-example`
 | 
			
		||||
- :ref:`fastapi-example`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										100
									
								
								examples/miniapps/flask-blueprints/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								examples/miniapps/flask-blueprints/README.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
Flask Blueprints + Dependency Injector Example
 | 
			
		||||
==============================================
 | 
			
		||||
 | 
			
		||||
This is a `Flask <https://flask.palletsprojects.com/>`_ Blueprints +
 | 
			
		||||
`Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example application.
 | 
			
		||||
 | 
			
		||||
The example application helps to search for repositories on the Github.
 | 
			
		||||
 | 
			
		||||
.. image:: screenshot.png
 | 
			
		||||
 | 
			
		||||
Run
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Create virtual environment:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   virtualenv venv
 | 
			
		||||
   . venv/bin/activate
 | 
			
		||||
 | 
			
		||||
Install requirements:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
To run the application do:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    export FLASK_APP=githubnavigator.application
 | 
			
		||||
    export FLASK_ENV=development
 | 
			
		||||
    flask run
 | 
			
		||||
 | 
			
		||||
The output should be something like:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
 | 
			
		||||
    * Serving Flask app "githubnavigator.application" (lazy loading)
 | 
			
		||||
    * Environment: development
 | 
			
		||||
    * Debug mode: on
 | 
			
		||||
    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 | 
			
		||||
    * Restarting with fsevents reloader
 | 
			
		||||
    * Debugger is active!
 | 
			
		||||
    * Debugger PIN: 473-587-859
 | 
			
		||||
 | 
			
		||||
After that visit http://127.0.0.1:5000/ in your browser.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   Github has a rate limit. When the rate limit is exceed you will see an exception
 | 
			
		||||
   ``github.GithubException.RateLimitExceededException``. For unauthenticated requests, the rate
 | 
			
		||||
   limit allows for up to 60 requests per hour. To extend the limit to 5000 requests per hour you
 | 
			
		||||
   need to set personal access token.
 | 
			
		||||
 | 
			
		||||
   It's easy:
 | 
			
		||||
 | 
			
		||||
   - Follow this `guide <https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token>`_ to create a token.
 | 
			
		||||
   - Set a token to the environment variable:
 | 
			
		||||
 | 
			
		||||
   .. code-block:: bash
 | 
			
		||||
 | 
			
		||||
      export GITHUB_TOKEN=<your token>
 | 
			
		||||
 | 
			
		||||
   - Restart the app with ``flask run``
 | 
			
		||||
 | 
			
		||||
   `Read more on Github rate limit <https://developer.github.com/v3/#rate-limiting>`_
 | 
			
		||||
 | 
			
		||||
Test
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
This application comes with the unit tests.
 | 
			
		||||
 | 
			
		||||
To run the tests do:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   py.test githubnavigator/tests.py --cov=githubnavigator
 | 
			
		||||
 | 
			
		||||
The output should be something like:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
 | 
			
		||||
   platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
 | 
			
		||||
   plugins: flask-1.0.0, cov-2.10.0
 | 
			
		||||
   collected 2 items
 | 
			
		||||
 | 
			
		||||
   githubnavigator/tests.py ..                                     [100%]
 | 
			
		||||
 | 
			
		||||
   ---------- coverage: platform darwin, python 3.8.3-final-0 -----------
 | 
			
		||||
   Name                             Stmts   Miss  Cover
 | 
			
		||||
   ----------------------------------------------------
 | 
			
		||||
   githubnavigator/__init__.py                 0      0   100%
 | 
			
		||||
   githubnavigator/application.py             15      0   100%
 | 
			
		||||
   githubnavigator/blueprints/example.py      12      0   100%
 | 
			
		||||
   githubnavigator/containers.py               7      0   100%
 | 
			
		||||
   githubnavigator/services.py                14      0   100%
 | 
			
		||||
   githubnavigator/tests.py                   34      0   100%
 | 
			
		||||
   -----------------------------------------------------------
 | 
			
		||||
   TOTAL                                      82      0   100%
 | 
			
		||||
							
								
								
									
										5
									
								
								examples/miniapps/flask-blueprints/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/miniapps/flask-blueprints/config.yml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
github:
 | 
			
		||||
  request_timeout: 10
 | 
			
		||||
default:
 | 
			
		||||
  query: "Dependency Injector"
 | 
			
		||||
  limit: 10
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
"""Top-level package."""
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
"""Application module."""
 | 
			
		||||
 | 
			
		||||
from flask import Flask
 | 
			
		||||
from flask_bootstrap import Bootstrap
 | 
			
		||||
 | 
			
		||||
from .containers import Container
 | 
			
		||||
from .blueprints import example
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_app() -> Flask:
 | 
			
		||||
    container = Container()
 | 
			
		||||
    container.config.from_yaml('config.yml')
 | 
			
		||||
    container.config.github.auth_token.from_env('GITHUB_TOKEN')
 | 
			
		||||
    container.wire(modules=[example])
 | 
			
		||||
 | 
			
		||||
    app = Flask(__name__)
 | 
			
		||||
    app.container = container
 | 
			
		||||
    app.register_blueprint(example.blueprint)
 | 
			
		||||
 | 
			
		||||
    bootstrap = Bootstrap()
 | 
			
		||||
    bootstrap.init_app(app)
 | 
			
		||||
 | 
			
		||||
    return app
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
"""Blueprints package."""
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
"""Example blueprint."""
 | 
			
		||||
 | 
			
		||||
from flask import Blueprint, request, render_template
 | 
			
		||||
from dependency_injector.wiring import inject, Provide
 | 
			
		||||
 | 
			
		||||
from githubnavigator.services import SearchService
 | 
			
		||||
from githubnavigator.containers import Container
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
blueprint = Blueprint('example', __name__, template_folder='templates/')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@blueprint.route('/')
 | 
			
		||||
@inject
 | 
			
		||||
def index(
 | 
			
		||||
        search_service: SearchService = Provide[Container.search_service],
 | 
			
		||||
        default_query: str = Provide[Container.config.default.query],
 | 
			
		||||
        default_limit: int = Provide[Container.config.default.limit.as_int()],
 | 
			
		||||
):
 | 
			
		||||
    query = request.args.get('query', default_query)
 | 
			
		||||
    limit = request.args.get('limit', default_limit, int)
 | 
			
		||||
 | 
			
		||||
    repositories = search_service.search_repositories(query, limit)
 | 
			
		||||
 | 
			
		||||
    return render_template(
 | 
			
		||||
        'index.html',
 | 
			
		||||
        query=query,
 | 
			
		||||
        limit=limit,
 | 
			
		||||
        repositories=repositories,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
"""Containers module."""
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
from github import Github
 | 
			
		||||
 | 
			
		||||
from . import services
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
"""Services module."""
 | 
			
		||||
 | 
			
		||||
from github import Github
 | 
			
		||||
from github.Repository import Repository
 | 
			
		||||
from github.Commit import Commit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SearchService:
 | 
			
		||||
    """Search service performs search on Github."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, github_client: Github):
 | 
			
		||||
        self._github_client = github_client
 | 
			
		||||
 | 
			
		||||
    def search_repositories(self, query, limit):
 | 
			
		||||
        """Search for repositories and return formatted data."""
 | 
			
		||||
        repositories = self._github_client.search_repositories(
 | 
			
		||||
            query=query,
 | 
			
		||||
            **{'in': 'name'},
 | 
			
		||||
        )
 | 
			
		||||
        return [
 | 
			
		||||
            self._format_repo(repository)
 | 
			
		||||
            for repository in repositories[:limit]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def _format_repo(self, repository: Repository):
 | 
			
		||||
        commits = repository.get_commits()
 | 
			
		||||
        return {
 | 
			
		||||
            'url': repository.html_url,
 | 
			
		||||
            'name': repository.name,
 | 
			
		||||
            'owner': {
 | 
			
		||||
                'login': repository.owner.login,
 | 
			
		||||
                'url': repository.owner.html_url,
 | 
			
		||||
                'avatar_url': repository.owner.avatar_url,
 | 
			
		||||
            },
 | 
			
		||||
            'latest_commit': self._format_commit(commits[0]) if commits else {},
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def _format_commit(self, commit: Commit):
 | 
			
		||||
        return {
 | 
			
		||||
            'sha': commit.sha,
 | 
			
		||||
            'url': commit.html_url,
 | 
			
		||||
            'message': commit.commit.message,
 | 
			
		||||
            'author_name': commit.commit.author.name,
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
    <head>
 | 
			
		||||
        {% block head %}
 | 
			
		||||
        <!-- Required meta tags -->
 | 
			
		||||
        <meta charset="utf-8">
 | 
			
		||||
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 | 
			
		||||
 | 
			
		||||
        {% block styles %}
 | 
			
		||||
            <!-- Bootstrap CSS -->
 | 
			
		||||
            {{ bootstrap.load_css() }}
 | 
			
		||||
        {% endblock %}
 | 
			
		||||
 | 
			
		||||
        <title>{% block title %}{% endblock %}</title>
 | 
			
		||||
        {% endblock %}
 | 
			
		||||
    </head>
 | 
			
		||||
    <body>
 | 
			
		||||
        <!-- Your page content -->
 | 
			
		||||
        {% block content %}{% endblock %}
 | 
			
		||||
 | 
			
		||||
        {% block scripts %}
 | 
			
		||||
            <!-- Optional JavaScript -->
 | 
			
		||||
            {{ bootstrap.load_js() }}
 | 
			
		||||
        {% endblock %}
 | 
			
		||||
    </body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
{% extends "base.html" %}
 | 
			
		||||
 | 
			
		||||
{% block title %}Github Navigator{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<div class="container">
 | 
			
		||||
    <h1 class="mb-4">Github Navigator</h1>
 | 
			
		||||
 | 
			
		||||
    <form>
 | 
			
		||||
        <div class="form-group form-row">
 | 
			
		||||
            <div class="col-10">
 | 
			
		||||
                <label for="search_query" class="col-form-label">
 | 
			
		||||
                    Search for:
 | 
			
		||||
                </label>
 | 
			
		||||
                <input class="form-control" type="text" id="search_query"
 | 
			
		||||
                       placeholder="Type something to search on the GitHub"
 | 
			
		||||
                       name="query"
 | 
			
		||||
                       value="{{ query if query }}">
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col">
 | 
			
		||||
                <label for="search_limit" class="col-form-label">
 | 
			
		||||
                    Limit:
 | 
			
		||||
                </label>
 | 
			
		||||
                <select class="form-control" id="search_limit" name="limit">
 | 
			
		||||
                    {% for value in [5, 10, 20] %}
 | 
			
		||||
                    <option {% if value == limit %}selected{% endif %}>
 | 
			
		||||
                        {{ value }}
 | 
			
		||||
                    </option>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
    <p><small>Results found: {{ repositories|length }}</small></p>
 | 
			
		||||
 | 
			
		||||
    <table class="table table-striped">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th>#</th>
 | 
			
		||||
                <th>Repository</th>
 | 
			
		||||
                <th class="text-nowrap">Repository owner</th>
 | 
			
		||||
                <th class="text-nowrap">Last commit</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
        {% for repository in repositories %} {{n}}
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th>{{ loop.index }}</th>
 | 
			
		||||
              <td><a href="{{ repository.url }}">
 | 
			
		||||
                  {{ repository.name }}</a>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td><a href="{{ repository.owner.url }}">
 | 
			
		||||
                  <img src="{{ repository.owner.avatar_url }}"
 | 
			
		||||
                       alt="avatar" height="24" width="24"/></a>
 | 
			
		||||
                  <a href="{{ repository.owner.url }}">
 | 
			
		||||
                      {{ repository.owner.login }}</a>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td><a href="{{ repository.latest_commit.url }}">
 | 
			
		||||
                  {{ repository.latest_commit.sha }}</a>
 | 
			
		||||
                  {{ repository.latest_commit.message }}
 | 
			
		||||
                  {{ repository.latest_commit.author_name }}
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										71
									
								
								examples/miniapps/flask-blueprints/githubnavigator/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								examples/miniapps/flask-blueprints/githubnavigator/tests.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
"""Tests module."""
 | 
			
		||||
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from github import Github
 | 
			
		||||
from flask import url_for
 | 
			
		||||
 | 
			
		||||
from .application import create_app
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def app():
 | 
			
		||||
    app = create_app()
 | 
			
		||||
    yield app
 | 
			
		||||
    app.container.unwire()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_index(client, app):
 | 
			
		||||
    github_client_mock = mock.Mock(spec=Github)
 | 
			
		||||
    github_client_mock.search_repositories.return_value = [
 | 
			
		||||
        mock.Mock(
 | 
			
		||||
            html_url='repo1-url',
 | 
			
		||||
            name='repo1-name',
 | 
			
		||||
            owner=mock.Mock(
 | 
			
		||||
                login='owner1-login',
 | 
			
		||||
                html_url='owner1-url',
 | 
			
		||||
                avatar_url='owner1-avatar-url',
 | 
			
		||||
            ),
 | 
			
		||||
            get_commits=mock.Mock(return_value=[mock.Mock()]),
 | 
			
		||||
        ),
 | 
			
		||||
        mock.Mock(
 | 
			
		||||
            html_url='repo2-url',
 | 
			
		||||
            name='repo2-name',
 | 
			
		||||
            owner=mock.Mock(
 | 
			
		||||
                login='owner2-login',
 | 
			
		||||
                html_url='owner2-url',
 | 
			
		||||
                avatar_url='owner2-avatar-url',
 | 
			
		||||
            ),
 | 
			
		||||
            get_commits=mock.Mock(return_value=[mock.Mock()]),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    with app.container.github_client.override(github_client_mock):
 | 
			
		||||
        response = client.get(url_for('example.index'))
 | 
			
		||||
 | 
			
		||||
    assert response.status_code == 200
 | 
			
		||||
    assert b'Results found: 2' in response.data
 | 
			
		||||
 | 
			
		||||
    assert b'repo1-url' in response.data
 | 
			
		||||
    assert b'repo1-name' in response.data
 | 
			
		||||
    assert b'owner1-login' in response.data
 | 
			
		||||
    assert b'owner1-url' in response.data
 | 
			
		||||
    assert b'owner1-avatar-url' in response.data
 | 
			
		||||
 | 
			
		||||
    assert b'repo2-url' in response.data
 | 
			
		||||
    assert b'repo2-name' in response.data
 | 
			
		||||
    assert b'owner2-login' in response.data
 | 
			
		||||
    assert b'owner2-url' in response.data
 | 
			
		||||
    assert b'owner2-avatar-url' in response.data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_index_no_results(client, app):
 | 
			
		||||
    github_client_mock = mock.Mock(spec=Github)
 | 
			
		||||
    github_client_mock.search_repositories.return_value = []
 | 
			
		||||
 | 
			
		||||
    with app.container.github_client.override(github_client_mock):
 | 
			
		||||
        response = client.get(url_for('example.index'))
 | 
			
		||||
 | 
			
		||||
    assert response.status_code == 200
 | 
			
		||||
    assert b'Results found: 0' in response.data
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/miniapps/flask-blueprints/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/miniapps/flask-blueprints/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
dependency-injector
 | 
			
		||||
flask
 | 
			
		||||
bootstrap-flask
 | 
			
		||||
pygithub
 | 
			
		||||
pyyaml
 | 
			
		||||
pytest-flask
 | 
			
		||||
pytest-cov
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/miniapps/flask-blueprints/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/miniapps/flask-blueprints/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 647 KiB  | 
| 
						 | 
				
			
			@ -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.9'
 | 
			
		||||
__version__ = '4.4.0'
 | 
			
		||||
"""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:
 | 
			
		||||
    if not _is_patched(fn):
 | 
			
		||||
        reference_injections, reference_closing = _fetch_reference_injections(fn)
 | 
			
		||||
        if not reference_injections:
 | 
			
		||||
            return
 | 
			
		||||
    patched = _patch_with_injections(fn, injections, closing)
 | 
			
		||||
    setattr(module, name, _wrap_patched(patched, fn, injections, closing))
 | 
			
		||||
        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