mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 01:47:36 +03:00 
			
		
		
		
	Merge branch 'release/4.6.0' into master
This commit is contained in:
		
						commit
						9cd201a493
					
				| 
						 | 
				
			
			@ -78,6 +78,15 @@ jobs:
 | 
			
		|||
      after_success:
 | 
			
		||||
        - python3 -m pip install --upgrade --upgrade-strategy eager twine
 | 
			
		||||
        - python3 -m twine upload wheelhouse/*.whl
 | 
			
		||||
    - services: docker
 | 
			
		||||
      arch: arm64
 | 
			
		||||
      if: tag IS present
 | 
			
		||||
      env: TWINE_USERNAME=__token__
 | 
			
		||||
      install: python3 -m pip install cibuildwheel==1.6.3
 | 
			
		||||
      script: python3 -m cibuildwheel --output-dir wheelhouse
 | 
			
		||||
      after_success:
 | 
			
		||||
        - python3 -m pip install --upgrade --upgrade-strategy eager twine
 | 
			
		||||
        - python3 -m twine upload wheelhouse/*.whl
 | 
			
		||||
    - os: osx
 | 
			
		||||
      if: tag IS present
 | 
			
		||||
      language: shell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,3 +14,4 @@ Dependency Injector Contributors
 | 
			
		|||
+ RobinsonMa (RobinsonMa)
 | 
			
		||||
+ Rüdiger Busche (JarnoRFB)
 | 
			
		||||
+ Dmitry Rassoshenko (rda-dev)
 | 
			
		||||
+ Fotis Koutoupas (kootoopas)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
Copyright (c) 2020, ETS Labs
 | 
			
		||||
Copyright (c) 2021, ETS Labs
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,6 +70,8 @@ Key features of the ``Dependency Injector``:
 | 
			
		|||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
 | 
			
		||||
  other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc.
 | 
			
		||||
  See `Wiring <https://python-dependency-injector.ets-labs.org/wiring.html>`_.
 | 
			
		||||
- **Asynchronous**. Supports asynchronous injections.
 | 
			
		||||
  See `Asynchronous injections <https://python-dependency-injector.ets-labs.org/providers/async.html>`_.
 | 
			
		||||
- **Typing**. Provides typing stubs, ``mypy``-friendly.
 | 
			
		||||
  See `Typing and mypy <https://python-dependency-injector.ets-labs.org/providers/typing_mypy.html>`_.
 | 
			
		||||
- **Performance**. Fast. Written in ``Cython``.
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +159,8 @@ Choose one of the following:
 | 
			
		|||
- `Flask example <https://python-dependency-injector.ets-labs.org/examples/flask.html>`_
 | 
			
		||||
- `Aiohttp example <https://python-dependency-injector.ets-labs.org/examples/aiohttp.html>`_
 | 
			
		||||
- `Sanic example <https://python-dependency-injector.ets-labs.org/examples/sanic.html>`_
 | 
			
		||||
- `FastAPI example <https://python-dependency-injector.ets-labs.org/examples/fastapi.html>`_
 | 
			
		||||
- `FastAPI + Redis example <https://python-dependency-injector.ets-labs.org/examples/fastapi-redis.html>`_
 | 
			
		||||
 | 
			
		||||
Tutorials
 | 
			
		||||
---------
 | 
			
		||||
| 
						 | 
				
			
			@ -223,4 +227,3 @@ Want to contribute?
 | 
			
		|||
.. |tell| unicode:: U+1F4AC .. tell sign
 | 
			
		||||
.. |fork| unicode:: U+1F500 .. fork sign
 | 
			
		||||
.. |pull| unicode:: U+2B05 U+FE0F .. pull sign
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ master_doc = 'index'
 | 
			
		|||
 | 
			
		||||
# General information about the project.
 | 
			
		||||
project = u'Dependency Injector'
 | 
			
		||||
copyright = u'2020, ETS Labs'
 | 
			
		||||
copyright = u'2021, ETS Labs'
 | 
			
		||||
author = u'ETS Labs'
 | 
			
		||||
 | 
			
		||||
# The version info for the project you're documenting, acts as replacement for
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										98
									
								
								docs/examples/fastapi-redis.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								docs/examples/fastapi-redis.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
.. _fastapi-redis-example:
 | 
			
		||||
 | 
			
		||||
FastAPI + Redis example
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
.. meta::
 | 
			
		||||
   :keywords: Python,Dependency Injection,FastAPI,Redis,Example
 | 
			
		||||
   :description: This example demonstrates a usage of the FastAPI, Redis, and Dependency Injector.
 | 
			
		||||
 | 
			
		||||
This example shows how to use ``Dependency Injector`` with `FastAPI <https://fastapi.tiangolo.com/>`_ and
 | 
			
		||||
`Redis <https://redis.io/>`_.
 | 
			
		||||
 | 
			
		||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-redis>`_.
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
- Provider :ref:`async-injections`
 | 
			
		||||
- Resource provider :ref:`resource-async-initializers`
 | 
			
		||||
- Wiring :ref:`async-injections-wiring`
 | 
			
		||||
 | 
			
		||||
Application structure
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
Application has next structure:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   ./
 | 
			
		||||
   ├── fastapiredis/
 | 
			
		||||
   │   ├── __init__.py
 | 
			
		||||
   │   ├── application.py
 | 
			
		||||
   │   ├── containers.py
 | 
			
		||||
   │   ├── redis.py
 | 
			
		||||
   │   ├── services.py
 | 
			
		||||
   │   └── tests.py
 | 
			
		||||
   ├── docker-compose.yml
 | 
			
		||||
   ├── Dockerfile
 | 
			
		||||
   └── requirements.txt
 | 
			
		||||
 | 
			
		||||
Redis
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Module ``redis`` defines Redis connection pool initialization and shutdown. See ``fastapiredis/redis.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/redis.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Service
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Module ``services`` contains example service. Service has a dependency on Redis connection pool.
 | 
			
		||||
It uses it for getting and setting a key asynchronously. Real life service will do something more meaningful.
 | 
			
		||||
See ``fastapiredis/services.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/services.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Container
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
Declarative container wires example service with Redis connection pool. See ``fastapiredis/containers.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/containers.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Application
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Module ``application`` creates ``FastAPI`` app, setup endpoint, and init container.
 | 
			
		||||
 | 
			
		||||
Endpoint ``index`` has a dependency on example service. The dependency is injected using :ref:`wiring` feature.
 | 
			
		||||
 | 
			
		||||
Listing of ``fastapiredis/application.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/application.py
 | 
			
		||||
   :language: python
 | 
			
		||||
 | 
			
		||||
Tests
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
Tests use :ref:`provider-overriding` feature to replace example service with a mock. See ``fastapiredis/tests.py``:
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/miniapps/fastapi-redis/fastapiredis/tests.py
 | 
			
		||||
   :language: python
 | 
			
		||||
   :emphasize-lines: 24
 | 
			
		||||
 | 
			
		||||
Sources
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
The source code is available on the `Github <https://github.com/ets-labs/python-dependency-injector/tree/master/examples/miniapps/fastapi-redis>`_.
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
- Provider :ref:`async-injections`
 | 
			
		||||
- Resource provider :ref:`resource-async-initializers`
 | 
			
		||||
- Wiring :ref:`async-injections-wiring`
 | 
			
		||||
 | 
			
		||||
.. disqus::
 | 
			
		||||
| 
						 | 
				
			
			@ -19,5 +19,6 @@ Explore the examples to see the ``Dependency Injector`` in action.
 | 
			
		|||
    aiohttp
 | 
			
		||||
    sanic
 | 
			
		||||
    fastapi
 | 
			
		||||
    fastapi-redis
 | 
			
		||||
 | 
			
		||||
.. disqus::
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,6 +78,7 @@ Key features of the ``Dependency Injector``:
 | 
			
		|||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
 | 
			
		||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
 | 
			
		||||
  other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc. See :ref:`wiring`.
 | 
			
		||||
- **Asynchronous**. Supports asynchronous injections. See :ref:`async-injections`.
 | 
			
		||||
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
 | 
			
		||||
- **Performance**. Fast. Written in ``Cython``.
 | 
			
		||||
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -287,6 +287,7 @@ Choose one of the following as a next step:
 | 
			
		|||
    - :ref:`aiohttp-example`
 | 
			
		||||
    - :ref:`sanic-example`
 | 
			
		||||
    - :ref:`fastapi-example`
 | 
			
		||||
    - :ref:`fastapi-redis-example`
 | 
			
		||||
- Pass the tutorials:
 | 
			
		||||
    - :ref:`flask-tutorial`
 | 
			
		||||
    - :ref:`aiohttp-tutorial`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ Key features of the ``Dependency Injector``:
 | 
			
		|||
- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`.
 | 
			
		||||
- **Wiring**. Injects dependencies into functions and methods. Helps integrating with
 | 
			
		||||
  other frameworks: Django, Flask, Aiohttp, Sanic, FastAPI, etc. See :ref:`wiring`.
 | 
			
		||||
- **Asynchronous**. Supports asynchronous injections. See :ref:`async-injections`.
 | 
			
		||||
- **Typing**. Provides typing stubs, ``mypy``-friendly. See :ref:`provider-typing`.
 | 
			
		||||
- **Performance**. Fast. Written in ``Cython``.
 | 
			
		||||
- **Maturity**. Mature and production-ready. Well-tested, documented and supported.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,19 @@ that were made in every particular version.
 | 
			
		|||
From version 0.7.6 *Dependency Injector* framework strictly 
 | 
			
		||||
follows `Semantic versioning`_
 | 
			
		||||
 | 
			
		||||
4.6.0
 | 
			
		||||
-----
 | 
			
		||||
- Add support of async injections for providers.
 | 
			
		||||
- Add support of async injections for wiring.
 | 
			
		||||
- Add support of async initializers for ``Resource`` provider.
 | 
			
		||||
- Add ``FastAPI`` + ``Redis`` example.
 | 
			
		||||
- Add ARM wheel builds.
 | 
			
		||||
  See issue `#342 <https://github.com/ets-labs/python-dependency-injector/issues/342>`_ for details.
 | 
			
		||||
- Fix a typo in `ext.flask` deprecation warning.
 | 
			
		||||
  See PR `#345 <https://github.com/ets-labs/python-dependency-injector/pull/345>`_ for details.
 | 
			
		||||
  Thanks to `Fotis Koutoupas <https://github.com/kootoopas>`_ for the fix.
 | 
			
		||||
- Update copyright year.
 | 
			
		||||
 | 
			
		||||
4.5.4
 | 
			
		||||
-----
 | 
			
		||||
- Fix manylinux wheels uploading issue.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										108
									
								
								docs/providers/async.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								docs/providers/async.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
.. _async-injections:
 | 
			
		||||
 | 
			
		||||
Asynchronous injections
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
.. meta::
 | 
			
		||||
   :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Providers,Async,Injections,Asynchronous,Await,
 | 
			
		||||
              Asyncio
 | 
			
		||||
   :description: Dependency Injector providers support asynchronous injections. This page
 | 
			
		||||
                 demonstrates how make asynchronous dependency injections in Python.
 | 
			
		||||
 | 
			
		||||
Providers support asynchronous injections.
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/providers/async.py
 | 
			
		||||
   :language: python
 | 
			
		||||
   :emphasize-lines: 26-29
 | 
			
		||||
   :lines: 3-
 | 
			
		||||
 | 
			
		||||
If provider has any awaitable injections it switches into async mode. In async mode provider always returns awaitable.
 | 
			
		||||
This causes a cascade effect:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   provider1()              <── Async mode enabled <──┐
 | 
			
		||||
   │                                                  │
 | 
			
		||||
   ├──> provider2()                                   │
 | 
			
		||||
   │                                                  │
 | 
			
		||||
   ├──> provider3()         <── Async mode enabled <──┤
 | 
			
		||||
   │    │                                             │
 | 
			
		||||
   │    └──> provider4()    <── Async provider ───────┘
 | 
			
		||||
   │
 | 
			
		||||
   └──> provider5()
 | 
			
		||||
        │
 | 
			
		||||
        └──> provider6()
 | 
			
		||||
 | 
			
		||||
In async mode provider prepares injections asynchronously.
 | 
			
		||||
 | 
			
		||||
If provider has multiple awaitable dependencies, it will run them concurrently. Provider will wait until all
 | 
			
		||||
dependencies are ready and inject them afterwards.
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   provider1()
 | 
			
		||||
   │
 | 
			
		||||
   ├──> provider2()         <── Async mode enabled
 | 
			
		||||
   │
 | 
			
		||||
   ├──> provider3()         <── Async mode enabled
 | 
			
		||||
   │
 | 
			
		||||
   └──> provider4()         <── Async mode enabled
 | 
			
		||||
 | 
			
		||||
Here is what provider will do for the previous example:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   injections = await asyncio.gather(
 | 
			
		||||
       provider2(),
 | 
			
		||||
       provider3(),
 | 
			
		||||
       provider4(),
 | 
			
		||||
   )
 | 
			
		||||
   await provider1(*injections)
 | 
			
		||||
 | 
			
		||||
Overriding behaviour
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
In async mode provider always returns awaitable. It applies to the overriding too. If provider in async mode is
 | 
			
		||||
overridden by a provider that doesn't return awaitable result, the result will be wrapped into awaitable.
 | 
			
		||||
 | 
			
		||||
.. literalinclude:: ../../examples/providers/async_overriding.py
 | 
			
		||||
   :language: python
 | 
			
		||||
   :emphasize-lines: 19-24
 | 
			
		||||
   :lines: 3-
 | 
			
		||||
 | 
			
		||||
Async mode mechanics and API
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
By default provider's async mode is undefined.
 | 
			
		||||
 | 
			
		||||
When provider async mode is undefined, provider will automatically select the mode during the next call.
 | 
			
		||||
If the result is awaitable, provider will enable async mode, if not - disable it.
 | 
			
		||||
 | 
			
		||||
If provider async mode is enabled, provider always returns awaitable. If the result is not awaitable,
 | 
			
		||||
provider wraps it into awaitable explicitly. You can safely ``await`` provider in async mode.
 | 
			
		||||
 | 
			
		||||
If provider async mode is disabled, provider behaves the regular way. It doesn't do async injections
 | 
			
		||||
preparation or non-awaitables to awaitables conversion.
 | 
			
		||||
 | 
			
		||||
Once provider async mode is enabled or disabled, provider will stay in this state. No automatic switching
 | 
			
		||||
will be done.
 | 
			
		||||
 | 
			
		||||
.. image:: images/async_mode.png
 | 
			
		||||
 | 
			
		||||
You can also use following methods to change provider's async mode manually:
 | 
			
		||||
 | 
			
		||||
- ``Provider.enable_async_mode()``
 | 
			
		||||
- ``Provider.disable_async_mode()``
 | 
			
		||||
- ``Provider.reset_async_mode()``
 | 
			
		||||
 | 
			
		||||
To check the state of provider's async mode use:
 | 
			
		||||
 | 
			
		||||
- ``Provider.is_async_mode_enabled()``
 | 
			
		||||
- ``Provider.is_async_mode_disabled()``
 | 
			
		||||
- ``Provider.is_async_mode_undefined()``
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
- Wiring :ref:`async-injections-wiring`
 | 
			
		||||
- Resource provider :ref:`resource-async-initializers`
 | 
			
		||||
- :ref:`fastapi-redis-example`
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/providers/images/async_mode.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/providers/images/async_mode.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 12 KiB  | 
| 
						 | 
				
			
			@ -51,4 +51,5 @@ Providers module API docs - :py:mod:`dependency_injector.providers`
 | 
			
		|||
    overriding
 | 
			
		||||
    provided_instance
 | 
			
		||||
    custom
 | 
			
		||||
    async
 | 
			
		||||
    typing_mypy
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ Resource provider
 | 
			
		|||
Resource providers help to initialize and configure logging, event loop, thread or process pool, etc.
 | 
			
		||||
 | 
			
		||||
Resource provider is similar to ``Singleton``. Resource initialization happens only once.
 | 
			
		||||
You can do injections and use provided instance the same way like you do with any other provider.
 | 
			
		||||
You can make injections and use provided instance the same way like you do with any other provider.
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
   :emphasize-lines: 12
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ You can do injections and use provided instance the same way like you do with an
 | 
			
		|||
           executor=thread_pool,
 | 
			
		||||
       )
 | 
			
		||||
 | 
			
		||||
Container has an interface to initialize and shutdown all resources:
 | 
			
		||||
Container has an interface to initialize and shutdown all resources at once:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ Container has an interface to initialize and shutdown all resources:
 | 
			
		|||
   container.init_resources()
 | 
			
		||||
   container.shutdown_resources()
 | 
			
		||||
 | 
			
		||||
You also can initialize and shutdown resources one-by-one using ``init()`` and
 | 
			
		||||
You can also initialize and shutdown resources one-by-one using ``init()`` and
 | 
			
		||||
``shutdown()`` methods of the provider:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +57,10 @@ You also can initialize and shutdown resources one-by-one using ``init()`` and
 | 
			
		|||
   container.thread_pool.init()
 | 
			
		||||
   container.thread_pool.shutdown()
 | 
			
		||||
 | 
			
		||||
When you call ``.shutdown()`` method on a resource provider, it will remove the reference to the initialized resource,
 | 
			
		||||
if any, and switch to uninitialized state. Some of resource initializer types support specifying custom
 | 
			
		||||
resource shutdown.
 | 
			
		||||
 | 
			
		||||
Resource provider supports 3 types of initializers:
 | 
			
		||||
 | 
			
		||||
- Function
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +101,7 @@ you configure global resource:
 | 
			
		|||
           fname='logging.ini',
 | 
			
		||||
       )
 | 
			
		||||
 | 
			
		||||
Function initializer does not support shutdown.
 | 
			
		||||
Function initializer does not provide a way to specify custom resource shutdown.
 | 
			
		||||
 | 
			
		||||
Generator initializer
 | 
			
		||||
---------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -235,4 +239,124 @@ The example above produces next output:
 | 
			
		|||
   Shutdown service
 | 
			
		||||
   127.0.0.1 - - [29/Oct/2020 22:39:41] "GET / HTTP/1.1" 200 -
 | 
			
		||||
 | 
			
		||||
.. _resource-async-initializers:
 | 
			
		||||
 | 
			
		||||
Asynchronous initializers
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
When you write an asynchronous application, you might need to initialize resources asynchronously. Resource
 | 
			
		||||
provider supports asynchronous initialization and shutdown.
 | 
			
		||||
 | 
			
		||||
Asynchronous function initializer:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   async def init_async_resource(argument1=..., argument2=...):
 | 
			
		||||
       return await connect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       resource = providers.Resource(
 | 
			
		||||
           init_resource,
 | 
			
		||||
           argument1=...,
 | 
			
		||||
           argument2=...,
 | 
			
		||||
       )
 | 
			
		||||
 | 
			
		||||
Asynchronous generator initializer:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   async def init_async_resource(argument1=..., argument2=...):
 | 
			
		||||
       connection = await connect()
 | 
			
		||||
       yield connection
 | 
			
		||||
       await connection.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       resource = providers.Resource(
 | 
			
		||||
           init_async_resource,
 | 
			
		||||
           argument1=...,
 | 
			
		||||
           argument2=...,
 | 
			
		||||
       )
 | 
			
		||||
 | 
			
		||||
Asynchronous subclass initializer:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   from dependency_injector import resources
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class AsyncConnection(resources.AsyncResource):
 | 
			
		||||
 | 
			
		||||
       async def init(self, argument1=..., argument2=...):
 | 
			
		||||
           yield await connect()
 | 
			
		||||
 | 
			
		||||
       async def shutdown(self, connection):
 | 
			
		||||
           await connection.close()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       resource = providers.Resource(
 | 
			
		||||
           AsyncConnection,
 | 
			
		||||
           argument1=...,
 | 
			
		||||
           argument2=...,
 | 
			
		||||
       )
 | 
			
		||||
 | 
			
		||||
When you use resource provider with asynchronous initializer you need to call its ``__call__()``,
 | 
			
		||||
``init()``, and ``shutdown()`` methods asynchronously:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import asyncio
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       connection = providers.Resource(init_async_connection)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   async def main():
 | 
			
		||||
       container = Container()
 | 
			
		||||
       connection = await container.connection()
 | 
			
		||||
       connection = await container.connection.init()
 | 
			
		||||
       connection = await container.connection.shutdown()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   if __name__ == '__main__':
 | 
			
		||||
       asyncio.run(main())
 | 
			
		||||
 | 
			
		||||
Container ``init_resources()`` and ``shutdown_resources()`` methods should be used asynchronously if there is
 | 
			
		||||
at least one asynchronous resource provider:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import asyncio
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       connection1 = providers.Resource(init_async_connection)
 | 
			
		||||
 | 
			
		||||
       connection2 = providers.Resource(init_sync_connection)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   async def main():
 | 
			
		||||
       container = Container()
 | 
			
		||||
       await container.init_resources()
 | 
			
		||||
       await container.shutdown_resources()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   if __name__ == '__main__':
 | 
			
		||||
       asyncio.run(main())
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
- Provider :ref:`async-injections`
 | 
			
		||||
- Wiring :ref:`async-injections-wiring`
 | 
			
		||||
- :ref:`fastapi-redis-example`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. disqus::
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,21 +167,105 @@ You can use that in testing to re-create and re-wire a container before each tes
 | 
			
		|||
   avoid re-wiring between tests.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
   Python has a limitation on patching already imported individual members. To protect from errors
 | 
			
		||||
   prefer an import of modules instead of individual members or make sure that imports happen
 | 
			
		||||
   Python has a limitation on patching individually imported functions. To protect from errors
 | 
			
		||||
   prefer importing modules to importing individual functions or make sure imports happen
 | 
			
		||||
   after the wiring:
 | 
			
		||||
 | 
			
		||||
   .. code-block:: python
 | 
			
		||||
 | 
			
		||||
      # Potential error:
 | 
			
		||||
 | 
			
		||||
      from .module import fn
 | 
			
		||||
 | 
			
		||||
      fn()
 | 
			
		||||
 | 
			
		||||
   Instead use next:
 | 
			
		||||
 | 
			
		||||
   .. code-block:: python
 | 
			
		||||
 | 
			
		||||
      # Always works:
 | 
			
		||||
 | 
			
		||||
      from . import module
 | 
			
		||||
 | 
			
		||||
      module.fn()
 | 
			
		||||
 | 
			
		||||
      # instead of
 | 
			
		||||
.. _async-injections-wiring:
 | 
			
		||||
 | 
			
		||||
      from .module import fn
 | 
			
		||||
Asynchronous injections
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
      fn()
 | 
			
		||||
Wiring feature supports asynchronous injections:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
       db = providers.Resource(init_async_db_client)
 | 
			
		||||
 | 
			
		||||
       cache = providers.Resource(init_async_cache_client)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   @inject
 | 
			
		||||
   async def main(
 | 
			
		||||
       db: Database = Provide[Container.db],
 | 
			
		||||
       cache: Cache = Provide[Container.cache],
 | 
			
		||||
   ):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
When you call asynchronous function wiring prepares injections asynchronously.
 | 
			
		||||
Here is what it does for previous example:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    db, cache = await asyncio.gather(
 | 
			
		||||
        container.db(),
 | 
			
		||||
        container.cache(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    await main(db=db, cache=cache)
 | 
			
		||||
 | 
			
		||||
You can also use ``Closing`` marker with the asynchronous ``Resource`` providers:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   @inject
 | 
			
		||||
   async def main(
 | 
			
		||||
       db: Database = Closing[Provide[Container.db]],
 | 
			
		||||
       cache: Cache = Closing[Provide[Container.cache]],
 | 
			
		||||
   ):
 | 
			
		||||
       ...
 | 
			
		||||
 | 
			
		||||
Wiring does closing asynchronously:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    db, cache = await asyncio.gather(
 | 
			
		||||
        container.db(),
 | 
			
		||||
        container.cache(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    await main(db=db, cache=cache)
 | 
			
		||||
 | 
			
		||||
    await asyncio.gather(
 | 
			
		||||
        container.db.shutdown(),
 | 
			
		||||
        container.cache.shutdown(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
See :ref:`Resources, wiring and per-function execution scope <resource-provider-wiring-closing>` for
 | 
			
		||||
details on ``Closing`` marker.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
   Wiring does not not convert asynchronous injections to synchronous.
 | 
			
		||||
 | 
			
		||||
   It handles asynchronous injections only for ``async def`` functions. Asynchronous injections into
 | 
			
		||||
   synchronous ``def`` function still work, but you need to take care of awaitables by your own.
 | 
			
		||||
 | 
			
		||||
See also:
 | 
			
		||||
 | 
			
		||||
- Provider :ref:`async-injections`
 | 
			
		||||
- Resource provider :ref:`resource-async-initializers`
 | 
			
		||||
- :ref:`fastapi-redis-example`
 | 
			
		||||
 | 
			
		||||
Integration with other frameworks
 | 
			
		||||
---------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -211,5 +295,6 @@ Take a look at other application examples:
 | 
			
		|||
- :ref:`aiohttp-example`
 | 
			
		||||
- :ref:`sanic-example`
 | 
			
		||||
- :ref:`fastapi-example`
 | 
			
		||||
- :ref:`fastapi-redis-example`
 | 
			
		||||
 | 
			
		||||
.. disqus::
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								examples/miniapps/fastapi-redis/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/miniapps/fastapi-redis/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
FROM python:3.8-buster
 | 
			
		||||
 | 
			
		||||
ENV PYTHONUNBUFFERED=1
 | 
			
		||||
 | 
			
		||||
WORKDIR /code
 | 
			
		||||
COPY . /code/
 | 
			
		||||
 | 
			
		||||
RUN pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
CMD ["uvicorn", "fastapiredis.application:app", "--host", "0.0.0.0"]
 | 
			
		||||
							
								
								
									
										89
									
								
								examples/miniapps/fastapi-redis/README.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								examples/miniapps/fastapi-redis/README.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
FastAPI + Redis + Dependency Injector Example
 | 
			
		||||
=============================================
 | 
			
		||||
 | 
			
		||||
This is a `FastAPI <https://docs.python.org/3/library/asyncio.html>`_
 | 
			
		||||
+ `Redis <https://redis.io/>`_
 | 
			
		||||
+ `Dependency Injector <https://python-dependency-injector.ets-labs.org/>`_ example application.
 | 
			
		||||
 | 
			
		||||
Run
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Build the Docker image:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   docker-compose build
 | 
			
		||||
 | 
			
		||||
Run the docker-compose environment:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    docker-compose up
 | 
			
		||||
 | 
			
		||||
The output should be something like:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:14.115 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:14.115 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=1, just started
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:14.115 # Configuration loaded
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.116 * Running mode=standalone, port=6379.
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.116 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.116 # Server initialized
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.117 * Loading RDB produced by version 6.0.9
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.117 * RDB age 1 seconds
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.117 * RDB memory usage when created 0.77 Mb
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.117 * DB loaded from disk: 0.000 seconds
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:14.117 * Ready to accept connections
 | 
			
		||||
   redis_1    | 1:signal-handler (1609728137) Received SIGTERM scheduling shutdown...
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:17.984 # User requested shutdown...
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:17.984 # Redis is now ready to exit, bye bye...
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:22.035 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:22.035 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=1, just started
 | 
			
		||||
   redis_1    | 1:C 04 Jan 2021 02:42:22.035 # Configuration loaded
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * Running mode=standalone, port=6379.
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 # Server initialized
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * Loading RDB produced by version 6.0.9
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * RDB age 9 seconds
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * RDB memory usage when created 0.77 Mb
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * DB loaded from disk: 0.000 seconds
 | 
			
		||||
   redis_1    | 1:M 04 Jan 2021 02:42:22.037 * Ready to accept connections
 | 
			
		||||
   example_1  | INFO:     Started server process [1]
 | 
			
		||||
   example_1  | INFO:     Waiting for application startup.
 | 
			
		||||
   example_1  | INFO:     Application startup complete.
 | 
			
		||||
   example_1  | INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
 | 
			
		||||
 | 
			
		||||
Test
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
This application comes with the unit tests.
 | 
			
		||||
 | 
			
		||||
To run the tests do:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
   docker-compose run --rm example py.test fastapiredis/tests.py --cov=fastapiredis
 | 
			
		||||
 | 
			
		||||
The output should be something like:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
 | 
			
		||||
   platform linux -- Python 3.8.6, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
 | 
			
		||||
   rootdir: /code
 | 
			
		||||
   plugins: cov-2.10.1, asyncio-0.14.0
 | 
			
		||||
   collected 1 item
 | 
			
		||||
 | 
			
		||||
   fastapiredis/tests.py .                                         [100%]
 | 
			
		||||
 | 
			
		||||
   ----------- coverage: platform linux, python 3.8.6-final-0 -----------
 | 
			
		||||
   Name                          Stmts   Miss  Cover
 | 
			
		||||
   -------------------------------------------------
 | 
			
		||||
   fastapiredis/__init__.py          0      0   100%
 | 
			
		||||
   fastapiredis/application.py      15      0   100%
 | 
			
		||||
   fastapiredis/containers.py        6      0   100%
 | 
			
		||||
   fastapiredis/redis.py             7      4    43%
 | 
			
		||||
   fastapiredis/services.py          7      3    57%
 | 
			
		||||
   fastapiredis/tests.py            18      0   100%
 | 
			
		||||
   -------------------------------------------------
 | 
			
		||||
   TOTAL                            53      7    87%
 | 
			
		||||
							
								
								
									
										21
									
								
								examples/miniapps/fastapi-redis/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/miniapps/fastapi-redis/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
version: "3.7"
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
 | 
			
		||||
  example:
 | 
			
		||||
    build: .
 | 
			
		||||
    environment:
 | 
			
		||||
      REDIS_HOST: "redis"
 | 
			
		||||
      REDIS_PASSWORD: "password"
 | 
			
		||||
    ports:
 | 
			
		||||
      - "8000:8000"
 | 
			
		||||
    volumes:
 | 
			
		||||
      - "./:/code"
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - "redis"
 | 
			
		||||
 | 
			
		||||
  redis:
 | 
			
		||||
    image: redis
 | 
			
		||||
    command: ["redis-server",  "--requirepass", "password"]
 | 
			
		||||
    ports:
 | 
			
		||||
      - "6379:6379"
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/miniapps/fastapi-redis/fastapiredis/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/miniapps/fastapi-redis/fastapiredis/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
"""Top-level package."""
 | 
			
		||||
							
								
								
									
										25
									
								
								examples/miniapps/fastapi-redis/fastapiredis/application.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								examples/miniapps/fastapi-redis/fastapiredis/application.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
"""Application module."""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from fastapi import FastAPI, Depends
 | 
			
		||||
from dependency_injector.wiring import inject, Provide
 | 
			
		||||
 | 
			
		||||
from .containers import Container
 | 
			
		||||
from .services import Service
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
app = FastAPI()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.api_route('/')
 | 
			
		||||
@inject
 | 
			
		||||
async def index(service: Service = Depends(Provide[Container.service])):
 | 
			
		||||
    value = await service.process()
 | 
			
		||||
    return {'result': value}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
container = Container()
 | 
			
		||||
container.config.redis_host.from_env('REDIS_HOST', 'localhost')
 | 
			
		||||
container.config.redis_password.from_env('REDIS_PASSWORD', 'password')
 | 
			
		||||
container.wire(modules=[sys.modules[__name__]])
 | 
			
		||||
							
								
								
									
										21
									
								
								examples/miniapps/fastapi-redis/fastapiredis/containers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/miniapps/fastapi-redis/fastapiredis/containers.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
"""Containers module."""
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
 | 
			
		||||
from . import redis, services
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    config = providers.Configuration()
 | 
			
		||||
 | 
			
		||||
    redis_pool = providers.Resource(
 | 
			
		||||
        redis.init_redis_pool,
 | 
			
		||||
        host=config.redis_host,
 | 
			
		||||
        password=config.redis_password,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    service = providers.Factory(
 | 
			
		||||
        services.Service,
 | 
			
		||||
        redis=redis_pool,
 | 
			
		||||
    )
 | 
			
		||||
							
								
								
									
										12
									
								
								examples/miniapps/fastapi-redis/fastapiredis/redis.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/miniapps/fastapi-redis/fastapiredis/redis.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
"""Redis client module."""
 | 
			
		||||
 | 
			
		||||
from typing import AsyncIterator
 | 
			
		||||
 | 
			
		||||
from aioredis import create_redis_pool, Redis
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def init_redis_pool(host: str, password: str) -> AsyncIterator[Redis]:
 | 
			
		||||
    pool = await create_redis_pool(f'redis://{host}', password=password)
 | 
			
		||||
    yield pool
 | 
			
		||||
    pool.close()
 | 
			
		||||
    await pool.wait_closed()
 | 
			
		||||
							
								
								
									
										12
									
								
								examples/miniapps/fastapi-redis/fastapiredis/services.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/miniapps/fastapi-redis/fastapiredis/services.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
"""Services module."""
 | 
			
		||||
 | 
			
		||||
from aioredis import Redis
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Service:
 | 
			
		||||
    def __init__(self, redis: Redis) -> None:
 | 
			
		||||
        self._redis = redis
 | 
			
		||||
 | 
			
		||||
    async def process(self) -> str:
 | 
			
		||||
        await self._redis.set('my-key', 'value')
 | 
			
		||||
        return await self._redis.get('my-key', encoding='utf-8')
 | 
			
		||||
							
								
								
									
										28
									
								
								examples/miniapps/fastapi-redis/fastapiredis/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								examples/miniapps/fastapi-redis/fastapiredis/tests.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
"""Tests module."""
 | 
			
		||||
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from httpx import AsyncClient
 | 
			
		||||
 | 
			
		||||
from .application import app, container
 | 
			
		||||
from .services import Service
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def client(event_loop):
 | 
			
		||||
    client = AsyncClient(app=app, base_url='http://test')
 | 
			
		||||
    yield client
 | 
			
		||||
    event_loop.run_until_complete(client.aclose())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.asyncio
 | 
			
		||||
async def test_index(client):
 | 
			
		||||
    service_mock = mock.AsyncMock(spec=Service)
 | 
			
		||||
    service_mock.process.return_value = 'Foo'
 | 
			
		||||
 | 
			
		||||
    with container.service.override(service_mock):
 | 
			
		||||
        response = await client.get('/')
 | 
			
		||||
 | 
			
		||||
    assert response.status_code == 200
 | 
			
		||||
    assert response.json() == {'result': 'Foo'}
 | 
			
		||||
							
								
								
									
										10
									
								
								examples/miniapps/fastapi-redis/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/miniapps/fastapi-redis/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
dependency-injector
 | 
			
		||||
fastapi
 | 
			
		||||
uvicorn
 | 
			
		||||
aioredis
 | 
			
		||||
 | 
			
		||||
# For testing:
 | 
			
		||||
pytest
 | 
			
		||||
pytest-asyncio
 | 
			
		||||
pytest-cov
 | 
			
		||||
httpx
 | 
			
		||||
							
								
								
									
										37
									
								
								examples/providers/async.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								examples/providers/async.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
"""Asynchronous injections example."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def init_async_resource():
 | 
			
		||||
    await asyncio.sleep(0.1)
 | 
			
		||||
    yield 'Initialized'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Service:
 | 
			
		||||
    def __init__(self, resource):
 | 
			
		||||
        self.resource = resource
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    resource = providers.Resource(init_async_resource)
 | 
			
		||||
 | 
			
		||||
    service = providers.Factory(
 | 
			
		||||
        Service,
 | 
			
		||||
        resource=resource,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def main(container: Container):
 | 
			
		||||
    resource = await container.resource()
 | 
			
		||||
    service = await container.service()
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    container = Container()
 | 
			
		||||
 | 
			
		||||
    asyncio.run(main(container))
 | 
			
		||||
							
								
								
									
										32
									
								
								examples/providers/async_overriding.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								examples/providers/async_overriding.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
"""Provider overriding in async mode example."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def init_async_resource():
 | 
			
		||||
    return ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init_resource_mock():
 | 
			
		||||
    return ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    resource = providers.Resource(init_async_resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def main(container: Container):
 | 
			
		||||
    resource1 = await container.resource()
 | 
			
		||||
 | 
			
		||||
    container.resource.override(providers.Callable(init_resource_mock))
 | 
			
		||||
    resource2 = await container.resource()
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    container = Container()
 | 
			
		||||
 | 
			
		||||
    asyncio.run(main(container))
 | 
			
		||||
| 
						 | 
				
			
			@ -7,5 +7,8 @@ pydocstyle
 | 
			
		|||
sphinx_autobuild
 | 
			
		||||
pip
 | 
			
		||||
mypy
 | 
			
		||||
pyyaml
 | 
			
		||||
httpx
 | 
			
		||||
fastapi
 | 
			
		||||
 | 
			
		||||
-r requirements-ext.txt
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@ max_complexity = 10
 | 
			
		|||
exclude = types.py
 | 
			
		||||
per-file-ignores =
 | 
			
		||||
    examples/demo/*: F841
 | 
			
		||||
    examples/providers/async.py: F841
 | 
			
		||||
    examples/providers/async_overriding.py: F841
 | 
			
		||||
    examples/wiring/*: F841
 | 
			
		||||
 | 
			
		||||
[pydocstyle]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
"""Top-level package."""
 | 
			
		||||
 | 
			
		||||
__version__ = '4.5.4'
 | 
			
		||||
__version__ = '4.6.0'
 | 
			
		||||
"""Version number.
 | 
			
		||||
 | 
			
		||||
:type: str
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -11,3 +11,6 @@ cpdef bint is_container(object instance)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
cpdef object _check_provider_type(object container, object provider)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cpdef bint _isawaitable(object instance)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,17 @@
 | 
			
		|||
from types import ModuleType
 | 
			
		||||
from typing import Type, Dict, Tuple, Optional, Any, Union, ClassVar, Callable as _Callable, Iterable, TypeVar
 | 
			
		||||
from typing import (
 | 
			
		||||
    Type,
 | 
			
		||||
    Dict,
 | 
			
		||||
    Tuple,
 | 
			
		||||
    Optional,
 | 
			
		||||
    Any,
 | 
			
		||||
    Union,
 | 
			
		||||
    ClassVar,
 | 
			
		||||
    Callable as _Callable,
 | 
			
		||||
    Iterable,
 | 
			
		||||
    TypeVar,
 | 
			
		||||
    Awaitable,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from .providers import Provider
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,8 +37,8 @@ class Container:
 | 
			
		|||
    def resolve_provider_name(self, provider_to_resolve: Provider) -> Optional[str]: ...
 | 
			
		||||
    def wire(self, modules: Optional[Iterable[ModuleType]] = None, packages: Optional[Iterable[ModuleType]] = None) -> None: ...
 | 
			
		||||
    def unwire(self) -> None: ...
 | 
			
		||||
    def init_resources(self) -> None: ...
 | 
			
		||||
    def shutdown_resources(self) -> None: ...
 | 
			
		||||
    def init_resources(self) -> Optional[Awaitable]: ...
 | 
			
		||||
    def shutdown_resources(self) -> Optional[Awaitable]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DynamicContainer(Container): ...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,13 @@
 | 
			
		|||
"""Containers module."""
 | 
			
		||||
 | 
			
		||||
import inspect
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import asyncio
 | 
			
		||||
except ImportError:
 | 
			
		||||
    asyncio = None
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from .errors import Error
 | 
			
		||||
| 
						 | 
				
			
			@ -216,17 +222,33 @@ class DynamicContainer(object):
 | 
			
		|||
 | 
			
		||||
    def init_resources(self):
 | 
			
		||||
        """Initialize all container resources."""
 | 
			
		||||
        futures = []
 | 
			
		||||
        for provider in self.providers.values():
 | 
			
		||||
            if not isinstance(provider, Resource):
 | 
			
		||||
                continue
 | 
			
		||||
            provider.init()
 | 
			
		||||
 | 
			
		||||
            resource = provider.init()
 | 
			
		||||
 | 
			
		||||
            if _isawaitable(resource):
 | 
			
		||||
                futures.append(resource)
 | 
			
		||||
 | 
			
		||||
        if futures:
 | 
			
		||||
            return asyncio.gather(*futures)
 | 
			
		||||
 | 
			
		||||
    def shutdown_resources(self):
 | 
			
		||||
        """Shutdown all container resources."""
 | 
			
		||||
        futures = []
 | 
			
		||||
        for provider in self.providers.values():
 | 
			
		||||
            if not isinstance(provider, Resource):
 | 
			
		||||
                continue
 | 
			
		||||
            provider.shutdown()
 | 
			
		||||
 | 
			
		||||
            shutdown = provider.shutdown()
 | 
			
		||||
 | 
			
		||||
            if _isawaitable(shutdown):
 | 
			
		||||
                futures.append(shutdown)
 | 
			
		||||
 | 
			
		||||
        if futures:
 | 
			
		||||
            return asyncio.gather(*futures)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeclarativeContainerMetaClass(type):
 | 
			
		||||
| 
						 | 
				
			
			@ -494,3 +516,10 @@ cpdef object _check_provider_type(object container, object provider):
 | 
			
		|||
    if not isinstance(provider, container.provider_type):
 | 
			
		||||
        raise Error('{0} can contain only {1} '
 | 
			
		||||
                    'instances'.format(container, container.provider_type))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cpdef bint _isawaitable(object instance):
 | 
			
		||||
    try:
 | 
			
		||||
        return <bint> inspect.isawaitable(instance)
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return <bint> False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ from dependency_injector import providers, errors
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
warnings.warn(
 | 
			
		||||
    'Module "dependency_injector.ext.aiohttp" is deprecated since '
 | 
			
		||||
    'Module "dependency_injector.ext.flask" is deprecated since '
 | 
			
		||||
    'version 4.0.0. Use "dependency_injector.wiring" module instead.',
 | 
			
		||||
    category=DeprecationWarning,
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,5 +1,12 @@
 | 
			
		|||
"""Providers module."""
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import asyncio
 | 
			
		||||
except ImportError:
 | 
			
		||||
    asyncio = None
 | 
			
		||||
 | 
			
		||||
import inspect
 | 
			
		||||
 | 
			
		||||
cimport cython
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +14,7 @@ cimport cython
 | 
			
		|||
cdef class Provider(object):
 | 
			
		||||
    cdef tuple __overridden
 | 
			
		||||
    cdef Provider __last_overriding
 | 
			
		||||
    cdef int __async_mode
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs)
 | 
			
		||||
    cpdef void _copy_overridings(self, Provider copied, dict memo)
 | 
			
		||||
| 
						 | 
				
			
			@ -134,10 +142,10 @@ cdef class FactoryAggregate(Provider):
 | 
			
		|||
# Singleton providers
 | 
			
		||||
cdef class BaseSingleton(Provider):
 | 
			
		||||
    cdef Factory __instantiator
 | 
			
		||||
    cdef object __storage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class Singleton(BaseSingleton):
 | 
			
		||||
    cdef object __storage
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +155,6 @@ cdef class DelegatedSingleton(Singleton):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
cdef class ThreadSafeSingleton(BaseSingleton):
 | 
			
		||||
    cdef object __storage
 | 
			
		||||
    cdef object __storage_lock
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs)
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +165,6 @@ cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
cdef class ThreadLocalSingleton(BaseSingleton):
 | 
			
		||||
    cdef object __storage
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -331,30 +337,38 @@ cdef inline tuple __separate_prefixed_kwargs(dict kwargs):
 | 
			
		|||
 | 
			
		||||
@cython.boundscheck(False)
 | 
			
		||||
@cython.wraparound(False)
 | 
			
		||||
cdef inline tuple __provide_positional_args(
 | 
			
		||||
cdef inline object __provide_positional_args(
 | 
			
		||||
        tuple args,
 | 
			
		||||
        tuple inj_args,
 | 
			
		||||
        int inj_args_len,
 | 
			
		||||
):
 | 
			
		||||
    cdef int index
 | 
			
		||||
    cdef list positional_args
 | 
			
		||||
    cdef list positional_args = []
 | 
			
		||||
    cdef list awaitables = []
 | 
			
		||||
    cdef PositionalInjection injection
 | 
			
		||||
 | 
			
		||||
    if inj_args_len == 0:
 | 
			
		||||
        return args
 | 
			
		||||
 | 
			
		||||
    positional_args = list()
 | 
			
		||||
    for index in range(inj_args_len):
 | 
			
		||||
        injection = <PositionalInjection>inj_args[index]
 | 
			
		||||
        positional_args.append(__get_value(injection))
 | 
			
		||||
        value = __get_value(injection)
 | 
			
		||||
        positional_args.append(value)
 | 
			
		||||
 | 
			
		||||
        if __isawaitable(value):
 | 
			
		||||
            awaitables.append((index, value))
 | 
			
		||||
 | 
			
		||||
    positional_args.extend(args)
 | 
			
		||||
 | 
			
		||||
    return tuple(positional_args)
 | 
			
		||||
    if awaitables:
 | 
			
		||||
        return __awaitable_args_kwargs_future(positional_args, awaitables)
 | 
			
		||||
 | 
			
		||||
    return positional_args
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cython.boundscheck(False)
 | 
			
		||||
@cython.wraparound(False)
 | 
			
		||||
cdef inline dict __provide_keyword_args(
 | 
			
		||||
cdef inline object __provide_keyword_args(
 | 
			
		||||
        dict kwargs,
 | 
			
		||||
        tuple inj_kwargs,
 | 
			
		||||
        int inj_kwargs_len,
 | 
			
		||||
| 
						 | 
				
			
			@ -362,14 +376,18 @@ cdef inline dict __provide_keyword_args(
 | 
			
		|||
    cdef int index
 | 
			
		||||
    cdef object name
 | 
			
		||||
    cdef object value
 | 
			
		||||
    cdef dict prefixed
 | 
			
		||||
    cdef dict prefixed = {}
 | 
			
		||||
    cdef list awaitables = []
 | 
			
		||||
    cdef NamedInjection kw_injection
 | 
			
		||||
 | 
			
		||||
    if len(kwargs) == 0:
 | 
			
		||||
        for index in range(inj_kwargs_len):
 | 
			
		||||
            kw_injection = <NamedInjection>inj_kwargs[index]
 | 
			
		||||
            name = __get_name(kw_injection)
 | 
			
		||||
            kwargs[name] = __get_value(kw_injection)
 | 
			
		||||
            value = __get_value(kw_injection)
 | 
			
		||||
            kwargs[name] = value
 | 
			
		||||
            if __isawaitable(value):
 | 
			
		||||
                awaitables.append((name, value))
 | 
			
		||||
    else:
 | 
			
		||||
        kwargs, prefixed = __separate_prefixed_kwargs(kwargs)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -387,23 +405,77 @@ cdef inline dict __provide_keyword_args(
 | 
			
		|||
                value = __get_value(kw_injection)
 | 
			
		||||
 | 
			
		||||
            kwargs[name] = value
 | 
			
		||||
            if __isawaitable(value):
 | 
			
		||||
                awaitables.append((name, value))
 | 
			
		||||
 | 
			
		||||
    if awaitables:
 | 
			
		||||
        return __awaitable_args_kwargs_future(kwargs, awaitables)
 | 
			
		||||
 | 
			
		||||
    return kwargs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline object __awaitable_args_kwargs_future(object args, list awaitables):
 | 
			
		||||
    future_result = asyncio.Future()
 | 
			
		||||
 | 
			
		||||
    args_future = asyncio.Future()
 | 
			
		||||
    args_future.set_result((future_result, args, awaitables))
 | 
			
		||||
 | 
			
		||||
    args_ready = asyncio.gather(args_future, *[value for _, value in awaitables])
 | 
			
		||||
    args_ready.add_done_callback(__async_prepare_args_kwargs_callback)
 | 
			
		||||
    asyncio.ensure_future(args_ready)
 | 
			
		||||
 | 
			
		||||
    return future_result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline void __async_prepare_args_kwargs_callback(object future):
 | 
			
		||||
    (future_result, args, awaitables), *awaited = future.result()
 | 
			
		||||
    for value, (key, _) in zip(awaited, awaitables):
 | 
			
		||||
        args[key] = value
 | 
			
		||||
    future_result.set_result(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@cython.boundscheck(False)
 | 
			
		||||
@cython.wraparound(False)
 | 
			
		||||
cdef inline object __inject_attributes(
 | 
			
		||||
        object instance,
 | 
			
		||||
        tuple attributes,
 | 
			
		||||
        int attributes_len,
 | 
			
		||||
):
 | 
			
		||||
cdef inline object __provide_attributes(tuple attributes, int attributes_len):
 | 
			
		||||
    cdef NamedInjection attr_injection
 | 
			
		||||
    cdef dict attribute_injections = {}
 | 
			
		||||
    cdef list awaitables = []
 | 
			
		||||
 | 
			
		||||
    for index in range(attributes_len):
 | 
			
		||||
        attr_injection = <NamedInjection>attributes[index]
 | 
			
		||||
        setattr(instance,
 | 
			
		||||
                __get_name(attr_injection),
 | 
			
		||||
                __get_value(attr_injection))
 | 
			
		||||
        name = __get_name(attr_injection)
 | 
			
		||||
        value = __get_value(attr_injection)
 | 
			
		||||
        attribute_injections[name] = value
 | 
			
		||||
        if __isawaitable(value):
 | 
			
		||||
            awaitables.append((name, value))
 | 
			
		||||
 | 
			
		||||
    if awaitables:
 | 
			
		||||
        return __awaitable_args_kwargs_future(attribute_injections, awaitables)
 | 
			
		||||
 | 
			
		||||
    return attribute_injections
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline object __async_inject_attributes(future_instance, future_attributes):
 | 
			
		||||
    future_result = asyncio.Future()
 | 
			
		||||
 | 
			
		||||
    future = asyncio.Future()
 | 
			
		||||
    future.set_result(future_result)
 | 
			
		||||
 | 
			
		||||
    attributes_ready = asyncio.gather(future, future_instance, future_attributes)
 | 
			
		||||
    attributes_ready.add_done_callback(__async_inject_attributes_callback)
 | 
			
		||||
    asyncio.ensure_future(attributes_ready)
 | 
			
		||||
 | 
			
		||||
    return future_result
 | 
			
		||||
 | 
			
		||||
cdef inline void __async_inject_attributes_callback(future):
 | 
			
		||||
    future_result, instance, attributes = future.result()
 | 
			
		||||
    __inject_attributes(instance, attributes)
 | 
			
		||||
    future_result.set_result(instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline void __inject_attributes(object instance, dict attributes):
 | 
			
		||||
    for name, value in attributes.items():
 | 
			
		||||
        setattr(instance, name, value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline object __call(
 | 
			
		||||
| 
						 | 
				
			
			@ -411,25 +483,53 @@ cdef inline object __call(
 | 
			
		|||
        tuple context_args,
 | 
			
		||||
        tuple injection_args,
 | 
			
		||||
        int injection_args_len,
 | 
			
		||||
        dict kwargs,
 | 
			
		||||
        dict context_kwargs,
 | 
			
		||||
        tuple injection_kwargs,
 | 
			
		||||
        int injection_kwargs_len,
 | 
			
		||||
):
 | 
			
		||||
    cdef tuple positional_args
 | 
			
		||||
    cdef dict keyword_args
 | 
			
		||||
 | 
			
		||||
    positional_args = __provide_positional_args(
 | 
			
		||||
    args = __provide_positional_args(
 | 
			
		||||
        context_args,
 | 
			
		||||
        injection_args,
 | 
			
		||||
        injection_args_len,
 | 
			
		||||
    )
 | 
			
		||||
    keyword_args = __provide_keyword_args(
 | 
			
		||||
        kwargs,
 | 
			
		||||
    kwargs = __provide_keyword_args(
 | 
			
		||||
        context_kwargs,
 | 
			
		||||
        injection_kwargs,
 | 
			
		||||
        injection_kwargs_len,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return call(*positional_args, **keyword_args)
 | 
			
		||||
    args_awaitable = __isawaitable(args)
 | 
			
		||||
    kwargs_awaitable = __isawaitable(kwargs)
 | 
			
		||||
 | 
			
		||||
    if args_awaitable or kwargs_awaitable:
 | 
			
		||||
        if not args_awaitable:
 | 
			
		||||
            future = asyncio.Future()
 | 
			
		||||
            future.set_result(args)
 | 
			
		||||
            args = future
 | 
			
		||||
 | 
			
		||||
        if not kwargs_awaitable:
 | 
			
		||||
            future = asyncio.Future()
 | 
			
		||||
            future.set_result(kwargs)
 | 
			
		||||
            kwargs = future
 | 
			
		||||
 | 
			
		||||
        future_result = asyncio.Future()
 | 
			
		||||
 | 
			
		||||
        future = asyncio.Future()
 | 
			
		||||
        future.set_result((future_result, call))
 | 
			
		||||
 | 
			
		||||
        args_kwargs_ready = asyncio.gather(future, args, kwargs)
 | 
			
		||||
        args_kwargs_ready.add_done_callback(__async_call_callback)
 | 
			
		||||
        asyncio.ensure_future(args_kwargs_ready)
 | 
			
		||||
 | 
			
		||||
        return future_result
 | 
			
		||||
 | 
			
		||||
    return call(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline void __async_call_callback(object future):
 | 
			
		||||
    (future_result, call), args, kwargs = future.result()
 | 
			
		||||
    result = call(*args, **kwargs)
 | 
			
		||||
    future_result.set_result(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline object __callable_call(Callable self, tuple args, dict kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -450,8 +550,40 @@ cdef inline object __factory_call(Factory self, tuple args, dict kwargs):
 | 
			
		|||
    instance = __callable_call(self.__instantiator, args, kwargs)
 | 
			
		||||
 | 
			
		||||
    if self.__attributes_len > 0:
 | 
			
		||||
        __inject_attributes(instance,
 | 
			
		||||
                            self.__attributes,
 | 
			
		||||
                            self.__attributes_len)
 | 
			
		||||
        attributes = __provide_attributes(self.__attributes, self.__attributes_len)
 | 
			
		||||
 | 
			
		||||
        instance_awaitable = __isawaitable(instance)
 | 
			
		||||
        attributes_awaitable = __isawaitable(attributes)
 | 
			
		||||
 | 
			
		||||
        if instance_awaitable or attributes_awaitable:
 | 
			
		||||
            if not instance_awaitable:
 | 
			
		||||
                future = asyncio.Future()
 | 
			
		||||
                future.set_result(instance)
 | 
			
		||||
                instance = future
 | 
			
		||||
 | 
			
		||||
            if not attributes_awaitable:
 | 
			
		||||
                future = asyncio.Future()
 | 
			
		||||
                future.set_result(attributes)
 | 
			
		||||
                attributes = future
 | 
			
		||||
 | 
			
		||||
            return __async_inject_attributes(instance, attributes)
 | 
			
		||||
 | 
			
		||||
        __inject_attributes(instance, attributes)
 | 
			
		||||
 | 
			
		||||
    return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef bint __has_isawaitable = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef inline bint __isawaitable(object instance):
 | 
			
		||||
    global __has_isawaitable
 | 
			
		||||
 | 
			
		||||
    if __has_isawaitable is True:
 | 
			
		||||
        return inspect.isawaitable(instance)
 | 
			
		||||
 | 
			
		||||
    if hasattr(inspect, 'isawaitable'):
 | 
			
		||||
        __has_isawaitable = True
 | 
			
		||||
        return inspect.isawaitable(instance)
 | 
			
		||||
 | 
			
		||||
    return False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ from __future__ import annotations
 | 
			
		|||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import (
 | 
			
		||||
    Awaitable,
 | 
			
		||||
    TypeVar,
 | 
			
		||||
    Generic,
 | 
			
		||||
    Type,
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +15,7 @@ from typing import (
 | 
			
		|||
    Union,
 | 
			
		||||
    Coroutine as _Coroutine,
 | 
			
		||||
    Iterator as _Iterator,
 | 
			
		||||
    AsyncIterator as _AsyncIterator,
 | 
			
		||||
    Generator as _Generator,
 | 
			
		||||
    overload,
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +35,13 @@ class OverridingContext:
 | 
			
		|||
 | 
			
		||||
class Provider(Generic[T]):
 | 
			
		||||
    def __init__(self) -> None: ...
 | 
			
		||||
 | 
			
		||||
    @overload
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
 | 
			
		||||
    def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
 | 
			
		||||
 | 
			
		||||
    def __deepcopy__(self, memo: Optional[_Dict[Any, Any]]) -> Provider: ...
 | 
			
		||||
    def __str__(self) -> str: ...
 | 
			
		||||
    def __repr__(self) -> str: ...
 | 
			
		||||
| 
						 | 
				
			
			@ -49,30 +57,33 @@ class Provider(Generic[T]):
 | 
			
		|||
    def provider(self) -> Provider: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def provided(self) -> ProvidedInstance: ...
 | 
			
		||||
    def enable_async_mode(self) -> None: ...
 | 
			
		||||
    def disable_async_mode(self) -> None: ...
 | 
			
		||||
    def reset_async_mode(self) -> None: ...
 | 
			
		||||
    def is_async_mode_enabled(self) -> bool: ...
 | 
			
		||||
    def is_async_mode_disabled(self) -> bool: ...
 | 
			
		||||
    def is_async_mode_undefined(self) -> bool: ...
 | 
			
		||||
    def _copy_overridings(self, copied: Provider, memo: Optional[_Dict[Any, Any]]) -> None: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Object(Provider, Generic[T]):
 | 
			
		||||
class Object(Provider[T]):
 | 
			
		||||
    def __init__(self, provides: T) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Delegate(Provider):
 | 
			
		||||
class Delegate(Provider[Provider]):
 | 
			
		||||
    def __init__(self, provides: Provider) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> Provider: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def provides(self) -> Provider: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Dependency(Provider, Generic[T]):
 | 
			
		||||
class Dependency(Provider[T]):
 | 
			
		||||
    def __init__(self, instance_of: Type[T] = object) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def instance_of(self) -> Type[T]: ...
 | 
			
		||||
    def provided_by(self, provider: Provider) -> OverridingContext: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExternalDependency(Dependency): ...
 | 
			
		||||
class ExternalDependency(Dependency[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DependenciesContainer(Object):
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +93,8 @@ class DependenciesContainer(Object):
 | 
			
		|||
    def providers(self) -> _Dict[str, Provider]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Callable(Provider, Generic[T]):
 | 
			
		||||
class Callable(Provider[T]):
 | 
			
		||||
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def provides(self) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -93,16 +103,16 @@ class Callable(Provider, Generic[T]):
 | 
			
		|||
    def set_args(self, *args: Injection) -> Callable[T]: ...
 | 
			
		||||
    def clear_args(self) -> Callable[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def kwargs(self) -> _Dict[str, Injection]: ...
 | 
			
		||||
    def kwargs(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
 | 
			
		||||
    def clear_kwargs(self) -> Callable[T]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedCallable(Callable): ...
 | 
			
		||||
class DelegatedCallable(Callable[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractCallable(Callable):
 | 
			
		||||
class AbstractCallable(Callable[T]):
 | 
			
		||||
    def override(self, provider: Callable) -> OverridingContext: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,13 +120,13 @@ class CallableDelegate(Delegate):
 | 
			
		|||
    def __init__(self, callable: Callable) -> None: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Coroutine(Callable): ...
 | 
			
		||||
class Coroutine(Callable[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedCoroutine(Coroutine): ...
 | 
			
		||||
class DelegatedCoroutine(Coroutine[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractCoroutine(Coroutine):
 | 
			
		||||
class AbstractCoroutine(Coroutine[T]):
 | 
			
		||||
    def override(self, provider: Coroutine) -> OverridingContext: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -124,10 +134,9 @@ class CoroutineDelegate(Delegate):
 | 
			
		|||
    def __init__(self, coroutine: Coroutine) -> None: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConfigurationOption(Provider):
 | 
			
		||||
class ConfigurationOption(Provider[Any]):
 | 
			
		||||
    UNDEFINED: object
 | 
			
		||||
    def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
 | 
			
		||||
    def __getattr__(self, item: str) -> ConfigurationOption: ...
 | 
			
		||||
    def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +158,7 @@ class TypedConfigurationOption(Callable[T]):
 | 
			
		|||
    def option(self) -> ConfigurationOption: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Configuration(Object):
 | 
			
		||||
class Configuration(Object[Any]):
 | 
			
		||||
    DEFAULT_NAME: str = 'config'
 | 
			
		||||
    def __init__(self, name: str = DEFAULT_NAME, default: Optional[Any] = None) -> None: ...
 | 
			
		||||
    def __getattr__(self, item: str) -> ConfigurationOption: ...
 | 
			
		||||
| 
						 | 
				
			
			@ -165,10 +174,9 @@ class Configuration(Object):
 | 
			
		|||
    def from_env(self, name: str, default: Optional[Any] = None) -> None: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Factory(Provider, Generic[T]):
 | 
			
		||||
class Factory(Provider[T]):
 | 
			
		||||
    provided_type: Optional[Type]
 | 
			
		||||
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def cls(self) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -179,21 +187,21 @@ class Factory(Provider, Generic[T]):
 | 
			
		|||
    def set_args(self, *args: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_args(self) -> Factory[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def kwargs(self) -> _Dict[str, Injection]: ...
 | 
			
		||||
    def kwargs(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_kwargs(self) -> Factory[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def attributes(self) -> _Dict[str, Injection]: ...
 | 
			
		||||
    def attributes(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_attributes(self) -> Factory[T]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedFactory(Factory): ...
 | 
			
		||||
class DelegatedFactory(Factory[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractFactory(Factory):
 | 
			
		||||
class AbstractFactory(Factory[T]):
 | 
			
		||||
    def override(self, provider: Factory) -> OverridingContext: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -203,55 +211,60 @@ class FactoryDelegate(Delegate):
 | 
			
		|||
 | 
			
		||||
class FactoryAggregate(Provider):
 | 
			
		||||
    def __init__(self, **factories: Factory): ...
 | 
			
		||||
    def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
 | 
			
		||||
    def __getattr__(self, factory_name: str) -> Factory: ...
 | 
			
		||||
 | 
			
		||||
    @overload
 | 
			
		||||
    def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Awaitable[Any]: ...
 | 
			
		||||
    def async_(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Awaitable[Any]: ...
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def factories(self) -> _Dict[str, Factory]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseSingleton(Provider, Generic[T]):
 | 
			
		||||
class BaseSingleton(Provider[T]):
 | 
			
		||||
    provided_type = Optional[Type]
 | 
			
		||||
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def cls(self) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def args(self) -> Tuple[Injection]: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> Factory[T]: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_args(self) -> Factory[T]: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def clear_args(self) -> BaseSingleton[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def kwargs(self) -> _Dict[str, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_kwargs(self) -> Factory[T]: ...
 | 
			
		||||
    def kwargs(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def clear_kwargs(self) -> BaseSingleton[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def attributes(self) -> _Dict[str, Injection]: ...
 | 
			
		||||
    def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
 | 
			
		||||
    def clear_attributes(self) -> Factory[T]: ...
 | 
			
		||||
    def attributes(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def set_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
 | 
			
		||||
    def clear_attributes(self) -> BaseSingleton[T]: ...
 | 
			
		||||
    def reset(self) -> None: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Singleton(BaseSingleton): ...
 | 
			
		||||
class Singleton(BaseSingleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedSingleton(Singleton): ...
 | 
			
		||||
class DelegatedSingleton(Singleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ThreadSafeSingleton(Singleton): ...
 | 
			
		||||
class ThreadSafeSingleton(Singleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedThreadSafeSingleton(ThreadSafeSingleton): ...
 | 
			
		||||
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ThreadLocalSingleton(BaseSingleton): ...
 | 
			
		||||
class ThreadLocalSingleton(BaseSingleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedThreadLocalSingleton(ThreadLocalSingleton): ...
 | 
			
		||||
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AbstractSingleton(BaseSingleton):
 | 
			
		||||
class AbstractSingleton(BaseSingleton[T]):
 | 
			
		||||
    def override(self, provider: BaseSingleton) -> OverridingContext: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -259,19 +272,17 @@ class SingletonDelegate(Delegate):
 | 
			
		|||
    def __init__(self, factory: BaseSingleton): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class List(Provider):
 | 
			
		||||
class List(Provider[_List]):
 | 
			
		||||
    def __init__(self, *args: Injection): ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> _List[Any]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def args(self) -> Tuple[Injection]: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> List: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> List: ...
 | 
			
		||||
    def clear_args(self) -> List: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> List[T]: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> List[T]: ...
 | 
			
		||||
    def clear_args(self) -> List[T]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Dict(Provider):
 | 
			
		||||
class Dict(Provider[_Dict]):
 | 
			
		||||
    def __init__(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection): ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> _Dict[Any, Any]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def kwargs(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection) -> Dict: ...
 | 
			
		||||
| 
						 | 
				
			
			@ -279,42 +290,44 @@ class Dict(Provider):
 | 
			
		|||
    def clear_kwargs(self) -> Dict: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Resource(Provider, Generic[T]):
 | 
			
		||||
class Resource(Provider[T]):
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: _Callable[..., resources.Resource[T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    def __init__(self, initializer: Type[resources.Resource[T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: Type[resources.AsyncResource[T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: _Callable[..., _Iterator[T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: _Callable[..., _AsyncIterator[T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: _Callable[..., _Coroutine[Injection, Injection, T]], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    @overload
 | 
			
		||||
    def __init__(self, initializer: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def args(self) -> Tuple[Injection]: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> Resource: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> Resource: ...
 | 
			
		||||
    def clear_args(self) -> Resource: ...
 | 
			
		||||
    def add_args(self, *args: Injection) -> Resource[T]: ...
 | 
			
		||||
    def set_args(self, *args: Injection) -> Resource[T]: ...
 | 
			
		||||
    def clear_args(self) -> Resource[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def kwargs(self) -> _Dict[Any, Injection]: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> Resource: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> Resource: ...
 | 
			
		||||
    def clear_kwargs(self) -> Resource: ...
 | 
			
		||||
    def add_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
 | 
			
		||||
    def set_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
 | 
			
		||||
    def clear_kwargs(self) -> Resource[T]: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def initialized(self) -> bool: ...
 | 
			
		||||
    def init(self) -> T: ...
 | 
			
		||||
    def shutdown(self) -> None: ...
 | 
			
		||||
    def init(self) -> Optional[Awaitable[T]]: ...
 | 
			
		||||
    def shutdown(self) -> Optional[Awaitable]: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(Provider):
 | 
			
		||||
 | 
			
		||||
class Container(Provider[T]):
 | 
			
		||||
    def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Provider) -> None: ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
 | 
			
		||||
    def __getattr__(self, name: str) -> Provider: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def container(self) -> T: ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Selector(Provider):
 | 
			
		||||
class Selector(Provider[Any]):
 | 
			
		||||
    def __init__(self, selector: _Callable[..., Any], **providers: Provider): ...
 | 
			
		||||
    def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
 | 
			
		||||
    def __getattr__(self, name: str) -> Provider: ...
 | 
			
		||||
    @property
 | 
			
		||||
    def providers(self) -> _Dict[str, Provider]: ...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import functools
 | 
			
		||||
import inspect
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +89,11 @@ else:
 | 
			
		|||
            return parser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef int ASYNC_MODE_UNDEFINED = 0
 | 
			
		||||
cdef int ASYNC_MODE_ENABLED = 1
 | 
			
		||||
cdef int ASYNC_MODE_DISABLED = 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class Provider(object):
 | 
			
		||||
    """Base provider class.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +154,7 @@ cdef class Provider(object):
 | 
			
		|||
        """Initializer."""
 | 
			
		||||
        self.__overridden = tuple()
 | 
			
		||||
        self.__last_overriding = None
 | 
			
		||||
        self.__async_mode = ASYNC_MODE_UNDEFINED
 | 
			
		||||
        super(Provider, self).__init__()
 | 
			
		||||
 | 
			
		||||
    def __call__(self, *args, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -156,8 +163,24 @@ cdef class Provider(object):
 | 
			
		|||
        Callable interface implementation.
 | 
			
		||||
        """
 | 
			
		||||
        if self.__last_overriding is not None:
 | 
			
		||||
            return self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        return self._provide(args, kwargs)
 | 
			
		||||
            result = self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        else:
 | 
			
		||||
            result = self._provide(args, kwargs)
 | 
			
		||||
 | 
			
		||||
        if self.is_async_mode_disabled():
 | 
			
		||||
            return result
 | 
			
		||||
        elif self.is_async_mode_enabled():
 | 
			
		||||
            if not __isawaitable(result):
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                future_result.set_result(result)
 | 
			
		||||
                return future_result
 | 
			
		||||
            return result
 | 
			
		||||
        elif self.is_async_mode_undefined():
 | 
			
		||||
            if __isawaitable(result):
 | 
			
		||||
                self.enable_async_mode()
 | 
			
		||||
            else:
 | 
			
		||||
                self.disable_async_mode()
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
    def __deepcopy__(self, memo):
 | 
			
		||||
        """Create and return full copy of provider."""
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +277,23 @@ cdef class Provider(object):
 | 
			
		|||
            self.__overridden = tuple()
 | 
			
		||||
            self.__last_overriding = None
 | 
			
		||||
 | 
			
		||||
    def async_(self, *args, **kwargs):
 | 
			
		||||
        """Return provided object asynchronously.
 | 
			
		||||
 | 
			
		||||
        This method is a synonym of __call__().
 | 
			
		||||
        It provides typing stubs for correct type checking with
 | 
			
		||||
        `await` expression:
 | 
			
		||||
 | 
			
		||||
        .. code-block:: python
 | 
			
		||||
 | 
			
		||||
            database_provider: Provider[DatabaseConnection] = Resource(init_db_async)
 | 
			
		||||
 | 
			
		||||
            async def main():
 | 
			
		||||
                db: DatabaseConnection = await database_provider.async_()
 | 
			
		||||
                ...
 | 
			
		||||
        """
 | 
			
		||||
        return self.__call__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def delegate(self):
 | 
			
		||||
        """Return provider's delegate.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -279,6 +319,33 @@ cdef class Provider(object):
 | 
			
		|||
        """Return :py:class:`ProvidedInstance` provider."""
 | 
			
		||||
        return ProvidedInstance(self)
 | 
			
		||||
 | 
			
		||||
    def enable_async_mode(self):
 | 
			
		||||
        """Enable async mode."""
 | 
			
		||||
        self.__async_mode = ASYNC_MODE_ENABLED
 | 
			
		||||
 | 
			
		||||
    def disable_async_mode(self):
 | 
			
		||||
        """Disable async mode."""
 | 
			
		||||
        self.__async_mode = ASYNC_MODE_DISABLED
 | 
			
		||||
 | 
			
		||||
    def reset_async_mode(self):
 | 
			
		||||
        """Reset async mode.
 | 
			
		||||
 | 
			
		||||
        Provider will automatically set the mode on the next call.
 | 
			
		||||
        """
 | 
			
		||||
        self.__async_mode = ASYNC_MODE_UNDEFINED
 | 
			
		||||
 | 
			
		||||
    def is_async_mode_enabled(self):
 | 
			
		||||
        """Check if async mode is enabled."""
 | 
			
		||||
        return self.__async_mode == ASYNC_MODE_ENABLED
 | 
			
		||||
 | 
			
		||||
    def is_async_mode_disabled(self):
 | 
			
		||||
        """Check if async mode is disabled."""
 | 
			
		||||
        return self.__async_mode == ASYNC_MODE_DISABLED
 | 
			
		||||
 | 
			
		||||
    def is_async_mode_undefined(self):
 | 
			
		||||
        """Check if async mode is undefined."""
 | 
			
		||||
        return self.__async_mode == ASYNC_MODE_UNDEFINED
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        """Providing strategy implementation.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -472,18 +539,38 @@ cdef class Dependency(Provider):
 | 
			
		|||
 | 
			
		||||
        :rtype: object
 | 
			
		||||
        """
 | 
			
		||||
        cdef object instance
 | 
			
		||||
 | 
			
		||||
        if self.__last_overriding is None:
 | 
			
		||||
            raise Error('Dependency is not defined')
 | 
			
		||||
 | 
			
		||||
        instance = self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        result = self.__last_overriding(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        if not isinstance(instance, self.instance_of):
 | 
			
		||||
            raise Error('{0} is not an '.format(instance) +
 | 
			
		||||
                        'instance of {0}'.format(self.instance_of))
 | 
			
		||||
 | 
			
		||||
        return instance
 | 
			
		||||
        if self.is_async_mode_disabled():
 | 
			
		||||
            self._check_instance_type(result)
 | 
			
		||||
            return result
 | 
			
		||||
        elif self.is_async_mode_enabled():
 | 
			
		||||
            if __isawaitable(result):
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                result = asyncio.ensure_future(result)
 | 
			
		||||
                result.add_done_callback(functools.partial(self._async_provide, future_result))
 | 
			
		||||
                return future_result
 | 
			
		||||
            else:
 | 
			
		||||
                self._check_instance_type(result)
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                future_result.set_result(result)
 | 
			
		||||
                return future_result
 | 
			
		||||
        elif self.is_async_mode_undefined():
 | 
			
		||||
            if __isawaitable(result):
 | 
			
		||||
                self.enable_async_mode()
 | 
			
		||||
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                result = asyncio.ensure_future(result)
 | 
			
		||||
                result.add_done_callback(functools.partial(self._async_provide, future_result))
 | 
			
		||||
                return future_result
 | 
			
		||||
            else:
 | 
			
		||||
                self.disable_async_mode()
 | 
			
		||||
                self._check_instance_type(result)
 | 
			
		||||
                return result
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        """Return string representation of provider.
 | 
			
		||||
| 
						 | 
				
			
			@ -514,6 +601,19 @@ cdef class Dependency(Provider):
 | 
			
		|||
        """
 | 
			
		||||
        return self.override(provider)
 | 
			
		||||
 | 
			
		||||
    def _async_provide(self, future_result, future):
 | 
			
		||||
        instance = future.result()
 | 
			
		||||
        try:
 | 
			
		||||
            self._check_instance_type(instance)
 | 
			
		||||
        except Error as exception:
 | 
			
		||||
            future_result.set_exception(exception)
 | 
			
		||||
        else:
 | 
			
		||||
            future_result.set_result(instance)
 | 
			
		||||
 | 
			
		||||
    def _check_instance_type(self, instance):
 | 
			
		||||
        if not isinstance(instance, self.instance_of):
 | 
			
		||||
            raise Error('{0} is not an instance of {1}'.format(instance, self.instance_of))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class ExternalDependency(Dependency):
 | 
			
		||||
    """:py:class:`ExternalDependency` provider describes dependency interface.
 | 
			
		||||
| 
						 | 
				
			
			@ -904,7 +1004,7 @@ cdef class AbstractCallable(Callable):
 | 
			
		|||
        """
 | 
			
		||||
        if self.__last_overriding is None:
 | 
			
		||||
            raise Error('{0} must be overridden before calling'.format(self))
 | 
			
		||||
        return self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        return super().__call__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def override(self, provider):
 | 
			
		||||
        """Override provider with another provider.
 | 
			
		||||
| 
						 | 
				
			
			@ -1020,7 +1120,7 @@ cdef class AbstractCoroutine(Coroutine):
 | 
			
		|||
        """
 | 
			
		||||
        if self.__last_overriding is None:
 | 
			
		||||
            raise Error('{0} must be overridden before calling'.format(self))
 | 
			
		||||
        return self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        return super().__call__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def override(self, provider):
 | 
			
		||||
        """Override provider with another provider.
 | 
			
		||||
| 
						 | 
				
			
			@ -1790,7 +1890,7 @@ cdef class AbstractFactory(Factory):
 | 
			
		|||
        """
 | 
			
		||||
        if self.__last_overriding is None:
 | 
			
		||||
            raise Error('{0} must be overridden before calling'.format(self))
 | 
			
		||||
        return self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        return super().__call__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def override(self, provider):
 | 
			
		||||
        """Override provider with another provider.
 | 
			
		||||
| 
						 | 
				
			
			@ -1881,13 +1981,6 @@ cdef class FactoryAggregate(Provider):
 | 
			
		|||
 | 
			
		||||
        return copied
 | 
			
		||||
 | 
			
		||||
    def __call__(self, factory_name, *args, **kwargs):
 | 
			
		||||
        """Create new object using factory with provided name.
 | 
			
		||||
 | 
			
		||||
        Callable interface implementation.
 | 
			
		||||
        """
 | 
			
		||||
        return self.__get_factory(factory_name)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, factory_name):
 | 
			
		||||
        """Return aggregated factory."""
 | 
			
		||||
        return self.__get_factory(factory_name)
 | 
			
		||||
| 
						 | 
				
			
			@ -1915,6 +2008,19 @@ cdef class FactoryAggregate(Provider):
 | 
			
		|||
        raise Error(
 | 
			
		||||
            '{0} providers could not be overridden'.format(self.__class__))
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
            factory_name = args[0]
 | 
			
		||||
        except IndexError:
 | 
			
		||||
            try:
 | 
			
		||||
                factory_name = kwargs.pop('factory_name')
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                raise TypeError('Factory missing 1 required positional argument: \'factory_name\'')
 | 
			
		||||
        else:
 | 
			
		||||
            args = args[1:]
 | 
			
		||||
 | 
			
		||||
        return self.__get_factory(factory_name)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    cdef Factory __get_factory(self, str factory_name):
 | 
			
		||||
        if factory_name not in self.__factories:
 | 
			
		||||
            raise NoSuchProviderError(
 | 
			
		||||
| 
						 | 
				
			
			@ -2075,6 +2181,16 @@ cdef class BaseSingleton(Provider):
 | 
			
		|||
        """
 | 
			
		||||
        raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
    def _async_init_instance(self, future_result, result):
 | 
			
		||||
        try:
 | 
			
		||||
            instance = result.result()
 | 
			
		||||
        except Exception as exception:
 | 
			
		||||
            self.__storage = None
 | 
			
		||||
            future_result.set_exception(exception)
 | 
			
		||||
        else:
 | 
			
		||||
            self.__storage = instance
 | 
			
		||||
            future_result.set_result(instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class Singleton(BaseSingleton):
 | 
			
		||||
    """Singleton provider returns same instance on every call.
 | 
			
		||||
| 
						 | 
				
			
			@ -2122,13 +2238,24 @@ cdef class Singleton(BaseSingleton):
 | 
			
		|||
 | 
			
		||||
        :rtype: None
 | 
			
		||||
        """
 | 
			
		||||
        if __isawaitable(self.__storage):
 | 
			
		||||
            asyncio.ensure_future(self.__storage).cancel()
 | 
			
		||||
        self.__storage = None
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        """Return single instance."""
 | 
			
		||||
        if self.__storage is None:
 | 
			
		||||
            self.__storage = __factory_call(self.__instantiator,
 | 
			
		||||
                                            args, kwargs)
 | 
			
		||||
            instance = __factory_call(self.__instantiator, args, kwargs)
 | 
			
		||||
 | 
			
		||||
            if __isawaitable(instance):
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                instance = asyncio.ensure_future(instance)
 | 
			
		||||
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
 | 
			
		||||
                self.__storage = future_result
 | 
			
		||||
                return future_result
 | 
			
		||||
 | 
			
		||||
            self.__storage = instance
 | 
			
		||||
 | 
			
		||||
        return self.__storage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2179,18 +2306,30 @@ cdef class ThreadSafeSingleton(BaseSingleton):
 | 
			
		|||
        :rtype: None
 | 
			
		||||
        """
 | 
			
		||||
        with self.__storage_lock:
 | 
			
		||||
            if __isawaitable(self.__storage):
 | 
			
		||||
                asyncio.ensure_future(self.__storage).cancel()
 | 
			
		||||
            self.__storage = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        """Return single instance."""
 | 
			
		||||
        storage = self.__storage
 | 
			
		||||
        if storage is None:
 | 
			
		||||
        instance = self.__storage
 | 
			
		||||
 | 
			
		||||
        if instance is None:
 | 
			
		||||
            with self.__storage_lock:
 | 
			
		||||
                if self.__storage is None:
 | 
			
		||||
                    self.__storage = __factory_call(self.__instantiator,
 | 
			
		||||
                                                    args, kwargs)
 | 
			
		||||
                storage = self.__storage
 | 
			
		||||
        return storage
 | 
			
		||||
                    instance = __factory_call(self.__instantiator, args, kwargs)
 | 
			
		||||
 | 
			
		||||
                    if __isawaitable(instance):
 | 
			
		||||
                        future_result = asyncio.Future()
 | 
			
		||||
                        instance = asyncio.ensure_future(instance)
 | 
			
		||||
                        instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
 | 
			
		||||
                        self.__storage = future_result
 | 
			
		||||
                        return future_result
 | 
			
		||||
 | 
			
		||||
                    self.__storage = instance
 | 
			
		||||
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class DelegatedThreadSafeSingleton(ThreadSafeSingleton):
 | 
			
		||||
| 
						 | 
				
			
			@ -2248,6 +2387,8 @@ cdef class ThreadLocalSingleton(BaseSingleton):
 | 
			
		|||
 | 
			
		||||
        :rtype: None
 | 
			
		||||
        """
 | 
			
		||||
        if __isawaitable(self.__storage.instance):
 | 
			
		||||
            asyncio.ensure_future(self.__storage.instance).cancel()
 | 
			
		||||
        del self.__storage.instance
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -2258,10 +2399,28 @@ cdef class ThreadLocalSingleton(BaseSingleton):
 | 
			
		|||
            instance = self.__storage.instance
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            instance = __factory_call(self.__instantiator, args, kwargs)
 | 
			
		||||
 | 
			
		||||
            if __isawaitable(instance):
 | 
			
		||||
                future_result = asyncio.Future()
 | 
			
		||||
                instance = asyncio.ensure_future(instance)
 | 
			
		||||
                instance.add_done_callback(functools.partial(self._async_init_instance, future_result))
 | 
			
		||||
                self.__storage.instance = future_result
 | 
			
		||||
                return future_result
 | 
			
		||||
 | 
			
		||||
            self.__storage.instance = instance
 | 
			
		||||
        finally:
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
    def _async_init_instance(self, future_result, result):
 | 
			
		||||
        try:
 | 
			
		||||
            instance = result.result()
 | 
			
		||||
        except Exception as exception:
 | 
			
		||||
            del self.__storage.instance
 | 
			
		||||
            future_result.set_exception(exception)
 | 
			
		||||
        else:
 | 
			
		||||
            self.__storage.instance = instance
 | 
			
		||||
            future_result.set_result(instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class DelegatedThreadLocalSingleton(ThreadLocalSingleton):
 | 
			
		||||
    """Delegated thread-local singleton is injected "as is".
 | 
			
		||||
| 
						 | 
				
			
			@ -2302,7 +2461,7 @@ cdef class AbstractSingleton(BaseSingleton):
 | 
			
		|||
        """
 | 
			
		||||
        if self.__last_overriding is None:
 | 
			
		||||
            raise Error('{0} must be overridden before calling'.format(self))
 | 
			
		||||
        return self.__last_overriding(*args, **kwargs)
 | 
			
		||||
        return super().__call__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def override(self, provider):
 | 
			
		||||
        """Override provider with another provider.
 | 
			
		||||
| 
						 | 
				
			
			@ -2705,18 +2864,30 @@ cdef class Resource(Provider):
 | 
			
		|||
    def shutdown(self):
 | 
			
		||||
        """Shutdown resource."""
 | 
			
		||||
        if not self.__initialized:
 | 
			
		||||
            if self.is_async_mode_enabled():
 | 
			
		||||
                result = asyncio.Future()
 | 
			
		||||
                result.set_result(None)
 | 
			
		||||
                return result
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if self.__shutdowner:
 | 
			
		||||
            try:
 | 
			
		||||
                self.__shutdowner(self.__resource)
 | 
			
		||||
                shutdown = self.__shutdowner(self.__resource)
 | 
			
		||||
            except StopIteration:
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                if inspect.isawaitable(shutdown):
 | 
			
		||||
                    return self._create_shutdown_future(shutdown)
 | 
			
		||||
 | 
			
		||||
        self.__resource = None
 | 
			
		||||
        self.__initialized = False
 | 
			
		||||
        self.__shutdowner = None
 | 
			
		||||
 | 
			
		||||
        if self.is_async_mode_enabled():
 | 
			
		||||
            result = asyncio.Future()
 | 
			
		||||
            result.set_result(None)
 | 
			
		||||
            return result
 | 
			
		||||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        if self.__initialized:
 | 
			
		||||
            return self.__resource
 | 
			
		||||
| 
						 | 
				
			
			@ -2733,6 +2904,19 @@ cdef class Resource(Provider):
 | 
			
		|||
                self.__kwargs_len,
 | 
			
		||||
            )
 | 
			
		||||
            self.__shutdowner = initializer.shutdown
 | 
			
		||||
        elif self._is_async_resource_subclass(self.__initializer):
 | 
			
		||||
            initializer = self.__initializer()
 | 
			
		||||
            async_init = __call(
 | 
			
		||||
                initializer.init,
 | 
			
		||||
                args,
 | 
			
		||||
                self.__args,
 | 
			
		||||
                self.__args_len,
 | 
			
		||||
                kwargs,
 | 
			
		||||
                self.__kwargs,
 | 
			
		||||
                self.__kwargs_len,
 | 
			
		||||
            )
 | 
			
		||||
            self.__initialized = True
 | 
			
		||||
            return self._create_init_future(async_init, initializer.shutdown)
 | 
			
		||||
        elif inspect.isgeneratorfunction(self.__initializer):
 | 
			
		||||
            initializer = __call(
 | 
			
		||||
                self.__initializer,
 | 
			
		||||
| 
						 | 
				
			
			@ -2745,6 +2929,30 @@ cdef class Resource(Provider):
 | 
			
		|||
            )
 | 
			
		||||
            self.__resource = next(initializer)
 | 
			
		||||
            self.__shutdowner = initializer.send
 | 
			
		||||
        elif iscoroutinefunction(self.__initializer):
 | 
			
		||||
            initializer = __call(
 | 
			
		||||
                self.__initializer,
 | 
			
		||||
                args,
 | 
			
		||||
                self.__args,
 | 
			
		||||
                self.__args_len,
 | 
			
		||||
                kwargs,
 | 
			
		||||
                self.__kwargs,
 | 
			
		||||
                self.__kwargs_len,
 | 
			
		||||
            )
 | 
			
		||||
            self.__initialized = True
 | 
			
		||||
            return self._create_init_future(initializer)
 | 
			
		||||
        elif isasyncgenfunction(self.__initializer):
 | 
			
		||||
            initializer = __call(
 | 
			
		||||
                self.__initializer,
 | 
			
		||||
                args,
 | 
			
		||||
                self.__args,
 | 
			
		||||
                self.__args_len,
 | 
			
		||||
                kwargs,
 | 
			
		||||
                self.__kwargs,
 | 
			
		||||
                self.__kwargs_len,
 | 
			
		||||
            )
 | 
			
		||||
            self.__initialized = True
 | 
			
		||||
            return self._create_init_future(initializer.__anext__(), initializer.asend)
 | 
			
		||||
        elif callable(self.__initializer):
 | 
			
		||||
            self.__resource = __call(
 | 
			
		||||
                self.__initializer,
 | 
			
		||||
| 
						 | 
				
			
			@ -2761,6 +2969,45 @@ cdef class Resource(Provider):
 | 
			
		|||
        self.__initialized = True
 | 
			
		||||
        return self.__resource
 | 
			
		||||
 | 
			
		||||
    def _create_init_future(self, future, shutdowner=None):
 | 
			
		||||
        callback = self._async_init_callback
 | 
			
		||||
        if shutdowner:
 | 
			
		||||
            callback = functools.partial(callback, shutdowner=shutdowner)
 | 
			
		||||
 | 
			
		||||
        future = asyncio.ensure_future(future)
 | 
			
		||||
        future.add_done_callback(callback)
 | 
			
		||||
        self.__resource = future
 | 
			
		||||
 | 
			
		||||
        return future
 | 
			
		||||
 | 
			
		||||
    def _async_init_callback(self, initializer, shutdowner=None):
 | 
			
		||||
        try:
 | 
			
		||||
            resource = initializer.result()
 | 
			
		||||
        except Exception:
 | 
			
		||||
            self.__initialized = False
 | 
			
		||||
            raise
 | 
			
		||||
        else:
 | 
			
		||||
            self.__resource = resource
 | 
			
		||||
            self.__shutdowner = shutdowner
 | 
			
		||||
 | 
			
		||||
    def _create_shutdown_future(self, shutdown_future):
 | 
			
		||||
        future = asyncio.Future()
 | 
			
		||||
        shutdown_future = asyncio.ensure_future(shutdown_future)
 | 
			
		||||
        shutdown_future.add_done_callback(functools.partial(self._async_shutdown_callback, future))
 | 
			
		||||
        return future
 | 
			
		||||
 | 
			
		||||
    def _async_shutdown_callback(self, future_result, shutdowner):
 | 
			
		||||
        try:
 | 
			
		||||
            shutdowner.result()
 | 
			
		||||
        except StopAsyncIteration:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.__resource = None
 | 
			
		||||
        self.__initialized = False
 | 
			
		||||
        self.__shutdowner = None
 | 
			
		||||
 | 
			
		||||
        future_result.set_result(None)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _is_resource_subclass(instance):
 | 
			
		||||
        if  sys.version_info < (3, 5):
 | 
			
		||||
| 
						 | 
				
			
			@ -2770,6 +3017,15 @@ cdef class Resource(Provider):
 | 
			
		|||
        from . import resources
 | 
			
		||||
        return issubclass(instance, resources.Resource)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _is_async_resource_subclass(instance):
 | 
			
		||||
        if  sys.version_info < (3, 5):
 | 
			
		||||
            return False
 | 
			
		||||
        if not isinstance(instance, CLASS_TYPES):
 | 
			
		||||
            return
 | 
			
		||||
        from . import resources
 | 
			
		||||
        return issubclass(instance, resources.AsyncResource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class Container(Provider):
 | 
			
		||||
    """Container provider provides an instance of declarative container.
 | 
			
		||||
| 
						 | 
				
			
			@ -3037,8 +3293,18 @@ cdef class AttributeGetter(Provider):
 | 
			
		|||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        provided = self.__provider(*args, **kwargs)
 | 
			
		||||
        if __isawaitable(provided):
 | 
			
		||||
            future_result = asyncio.Future()
 | 
			
		||||
            provided = asyncio.ensure_future(provided)
 | 
			
		||||
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
 | 
			
		||||
            return future_result
 | 
			
		||||
        return getattr(provided, self.__attribute)
 | 
			
		||||
 | 
			
		||||
    def _async_provide(self, future_result, future):
 | 
			
		||||
        provided = future.result()
 | 
			
		||||
        result = getattr(provided, self.__attribute)
 | 
			
		||||
        future_result.set_result(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class ItemGetter(Provider):
 | 
			
		||||
    """Provider that returns the item of the injected instance.
 | 
			
		||||
| 
						 | 
				
			
			@ -3087,8 +3353,18 @@ cdef class ItemGetter(Provider):
 | 
			
		|||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        provided = self.__provider(*args, **kwargs)
 | 
			
		||||
        if __isawaitable(provided):
 | 
			
		||||
            future_result = asyncio.Future()
 | 
			
		||||
            provided = asyncio.ensure_future(provided)
 | 
			
		||||
            provided.add_done_callback(functools.partial(self._async_provide, future_result))
 | 
			
		||||
            return future_result
 | 
			
		||||
        return provided[self.__item]
 | 
			
		||||
 | 
			
		||||
    def _async_provide(self, future_result, future):
 | 
			
		||||
        provided = future.result()
 | 
			
		||||
        result = provided[self.__item]
 | 
			
		||||
        future_result.set_result(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class MethodCaller(Provider):
 | 
			
		||||
    """Provider that calls the method of the injected instance.
 | 
			
		||||
| 
						 | 
				
			
			@ -3169,6 +3445,11 @@ cdef class MethodCaller(Provider):
 | 
			
		|||
 | 
			
		||||
    cpdef object _provide(self, tuple args, dict kwargs):
 | 
			
		||||
        call = self.__provider()
 | 
			
		||||
        if __isawaitable(call):
 | 
			
		||||
            future_result = asyncio.Future()
 | 
			
		||||
            call = asyncio.ensure_future(call)
 | 
			
		||||
            call.add_done_callback(functools.partial(self._async_provide, future_result, args, kwargs))
 | 
			
		||||
            return future_result
 | 
			
		||||
        return __call(
 | 
			
		||||
            call,
 | 
			
		||||
            args,
 | 
			
		||||
| 
						 | 
				
			
			@ -3179,6 +3460,19 @@ cdef class MethodCaller(Provider):
 | 
			
		|||
            self.__kwargs_len,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _async_provide(self, future_result, args, kwargs, future):
 | 
			
		||||
        call = future.result()
 | 
			
		||||
        result = __call(
 | 
			
		||||
            call,
 | 
			
		||||
            args,
 | 
			
		||||
            self.__args,
 | 
			
		||||
            self.__args_len,
 | 
			
		||||
            kwargs,
 | 
			
		||||
            self.__kwargs,
 | 
			
		||||
            self.__kwargs_len,
 | 
			
		||||
        )
 | 
			
		||||
        future_result.set_result(result)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cdef class Injection(object):
 | 
			
		||||
    """Abstract injection class."""
 | 
			
		||||
| 
						 | 
				
			
			@ -3381,3 +3675,36 @@ def merge_dicts(dict1, dict2):
 | 
			
		|||
    result = dict1.copy()
 | 
			
		||||
    result.update(dict2)
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isawaitable(obj):
 | 
			
		||||
    """Check if object is a coroutine function.
 | 
			
		||||
 | 
			
		||||
    Return False for any object in Python 3.4 or below.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return inspect.isawaitable(obj)
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def iscoroutinefunction(obj):
 | 
			
		||||
    """Check if object is a coroutine function.
 | 
			
		||||
 | 
			
		||||
    Return False for any object in Python 3.4 or below.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return inspect.iscoroutinefunction(obj)
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isasyncgenfunction(obj):
 | 
			
		||||
    """Check if object is an asynchronous generator function.
 | 
			
		||||
 | 
			
		||||
    Return False for any object in Python 3.4 or below.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return inspect.isasyncgenfunction(obj)
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,3 +29,14 @@ class Resource(Generic[T], metaclass=ResourceMeta):
 | 
			
		|||
    @abc.abstractmethod
 | 
			
		||||
    def shutdown(self, resource: T) -> None:
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncResource(Generic[T], metaclass=ResourceMeta):
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    async def init(self, *args, **kwargs) -> T:
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    async def shutdown(self, resource: T) -> None:
 | 
			
		||||
        ...
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
"""Wiring module."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import functools
 | 
			
		||||
import inspect
 | 
			
		||||
import importlib
 | 
			
		||||
| 
						 | 
				
			
			@ -426,10 +427,20 @@ def _get_async_patched(fn):
 | 
			
		|||
    @functools.wraps(fn)
 | 
			
		||||
    async def _patched(*args, **kwargs):
 | 
			
		||||
        to_inject = kwargs.copy()
 | 
			
		||||
        to_inject_await = []
 | 
			
		||||
        to_close_await = []
 | 
			
		||||
        for injection, provider in _patched.__injections__.items():
 | 
			
		||||
            if injection not in kwargs \
 | 
			
		||||
                    or _is_fastapi_default_arg_injection(injection, kwargs):
 | 
			
		||||
                to_inject[injection] = provider()
 | 
			
		||||
                provide = provider()
 | 
			
		||||
                if inspect.isawaitable(provide):
 | 
			
		||||
                    to_inject_await.append((injection, provide))
 | 
			
		||||
                else:
 | 
			
		||||
                    to_inject[injection] = provide
 | 
			
		||||
 | 
			
		||||
        async_to_inject = await asyncio.gather(*[provide for _, provide in to_inject_await])
 | 
			
		||||
        for provide, (injection, _) in zip(async_to_inject, to_inject_await):
 | 
			
		||||
            to_inject[injection] = provide
 | 
			
		||||
 | 
			
		||||
        result = await fn(*args, **to_inject)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -439,7 +450,11 @@ def _get_async_patched(fn):
 | 
			
		|||
                continue
 | 
			
		||||
            if not isinstance(provider, providers.Resource):
 | 
			
		||||
                continue
 | 
			
		||||
            provider.shutdown()
 | 
			
		||||
            shutdown = provider.shutdown()
 | 
			
		||||
            if inspect.isawaitable(shutdown):
 | 
			
		||||
                to_close_await.append(shutdown)
 | 
			
		||||
 | 
			
		||||
        await asyncio.gather(*to_close_await)
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
    return _patched
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,3 +50,9 @@ animal7: Animal = provider7(1, 2, 3, b='1', c=2, e=0.0)
 | 
			
		|||
 | 
			
		||||
# Test 8: to check the CallableDelegate __init__
 | 
			
		||||
provider8 = providers.CallableDelegate(providers.Callable(lambda: None))
 | 
			
		||||
 | 
			
		||||
# Test 9: to check the return type with await
 | 
			
		||||
provider9 = providers.Callable(Cat)
 | 
			
		||||
async def _async9() -> None:
 | 
			
		||||
    animal1: Animal = await provider9(1, 2, 3, b='1', c=2, e=0.0)  # type: ignore
 | 
			
		||||
    animal2: Animal = await provider9.async_(1, 2, 3, b='1', c=2, e=0.0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,9 @@ from dependency_injector import providers
 | 
			
		|||
# Test 1: to check the return type
 | 
			
		||||
provider1 = providers.Delegate(providers.Provider())
 | 
			
		||||
var1: providers.Provider = provider1()
 | 
			
		||||
 | 
			
		||||
# Test 2: to check the return type with await
 | 
			
		||||
provider2 = providers.Delegate(providers.Provider())
 | 
			
		||||
async def _async2() -> None:
 | 
			
		||||
    var1: providers.Provider = await provider2()  # type: ignore
 | 
			
		||||
    var2: providers.Provider = await provider2.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,3 +20,9 @@ var1: Animal = provider1()
 | 
			
		|||
# Test 2: to check the return type
 | 
			
		||||
provider2 = providers.Dependency(instance_of=Animal)
 | 
			
		||||
var2: Type[Animal] = provider2.instance_of
 | 
			
		||||
 | 
			
		||||
# Test 3: to check the return type with await
 | 
			
		||||
provider3 = providers.Dependency(instance_of=Animal)
 | 
			
		||||
async def _async3() -> None:
 | 
			
		||||
    var1: Animal = await provider3()  # type: ignore
 | 
			
		||||
    var2: Animal = await provider3.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,3 +35,13 @@ provider5 = providers.Dict(
 | 
			
		|||
    a2=providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
provided5: providers.ProvidedInstance = provider5.provided
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test 6: to check the return type with await
 | 
			
		||||
provider6 = providers.Dict(
 | 
			
		||||
    a1=providers.Factory(object),
 | 
			
		||||
    a2=providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
async def _async3() -> None:
 | 
			
		||||
    var1: Dict[Any, Any] = await provider6()  # type: ignore
 | 
			
		||||
    var2: Dict[Any, Any] = await provider6.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,3 +66,9 @@ val9: Any = provider9('a')
 | 
			
		|||
# Test 10: to check the explicit typing
 | 
			
		||||
factory10: providers.Provider[Animal] = providers.Factory(Cat)
 | 
			
		||||
animal10: Animal = factory10()
 | 
			
		||||
 | 
			
		||||
# Test 11: to check the return type with await
 | 
			
		||||
provider11 = providers.Factory(Cat)
 | 
			
		||||
async def _async11() -> None:
 | 
			
		||||
    animal1: Animal = await provider11(1, 2, 3, b='1', c=2, e=0.0)  # type: ignore
 | 
			
		||||
    animal2: Animal = await provider11.async_(1, 2, 3, b='1', c=2, e=0.0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,3 +27,12 @@ provided3: providers.ProvidedInstance = provider3.provided
 | 
			
		|||
attr_getter3: providers.AttributeGetter = provider3.provided.attr
 | 
			
		||||
item_getter3: providers.ItemGetter = provider3.provided['item']
 | 
			
		||||
method_caller3: providers.MethodCaller = provider3.provided.method.call(123, arg=324)
 | 
			
		||||
 | 
			
		||||
# Test 4: to check the return type with await
 | 
			
		||||
provider4 = providers.List(
 | 
			
		||||
    providers.Factory(object),
 | 
			
		||||
    providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
async def _async4() -> None:
 | 
			
		||||
    var1: List[Any] = await provider4()  # type: ignore
 | 
			
		||||
    var2: List[Any] = await provider4.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,3 +11,9 @@ provided2: providers.ProvidedInstance = provider2.provided
 | 
			
		|||
attr_getter2: providers.AttributeGetter = provider2.provided.attr
 | 
			
		||||
item_getter2: providers.ItemGetter = provider2.provided['item']
 | 
			
		||||
method_caller2: providers.MethodCaller = provider2.provided.method.call(123, arg=324)
 | 
			
		||||
 | 
			
		||||
# Test 3: to check the return type with await
 | 
			
		||||
provider3 = providers.Object(int(3))
 | 
			
		||||
async def _async3() -> None:
 | 
			
		||||
    var1: int = await provider3()  # type: ignore
 | 
			
		||||
    var2: int = await provider3.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,12 @@ from dependency_injector import providers
 | 
			
		|||
# Test 1: to check .provided attribute
 | 
			
		||||
provider1: providers.Provider[int] = providers.Object(1)
 | 
			
		||||
provided: providers.ProvidedInstance = provider1.provided
 | 
			
		||||
 | 
			
		||||
# Test 2: to check async mode API
 | 
			
		||||
provider2: providers.Provider = providers.Provider()
 | 
			
		||||
provider2.enable_async_mode()
 | 
			
		||||
provider2.disable_async_mode()
 | 
			
		||||
provider2.reset_async_mode()
 | 
			
		||||
r1: bool = provider2.is_async_mode_enabled()
 | 
			
		||||
r2: bool = provider2.is_async_mode_disabled()
 | 
			
		||||
r3: bool = provider2.is_async_mode_undefined()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
from typing import List, Iterator, Generator
 | 
			
		||||
from typing import List, Iterator, Generator, AsyncIterator, AsyncGenerator
 | 
			
		||||
 | 
			
		||||
from dependency_injector import providers, resources
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,3 +41,59 @@ class MyResource4(resources.Resource[List[int]]):
 | 
			
		|||
 | 
			
		||||
provider4 = providers.Resource(MyResource4)
 | 
			
		||||
var4: List[int] = provider4()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test 5: to check the return type with async function
 | 
			
		||||
async def init5() -> List[int]:
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
provider5 = providers.Resource(init5)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _provide5() -> None:
 | 
			
		||||
    var1: List[int] = await provider5()  # type: ignore
 | 
			
		||||
    var2: List[int] = await provider5.async_()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test 6: to check the return type with async iterator
 | 
			
		||||
async def init6() -> AsyncIterator[List[int]]:
 | 
			
		||||
    yield []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
provider6 = providers.Resource(init6)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _provide6() -> None:
 | 
			
		||||
    var1: List[int] = await provider6()  # type: ignore
 | 
			
		||||
    var2: List[int] = await provider6.async_()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test 7: to check the return type with async generator
 | 
			
		||||
async def init7() -> AsyncGenerator[List[int], None]:
 | 
			
		||||
    yield []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
provider7 = providers.Resource(init7)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _provide7() -> None:
 | 
			
		||||
    var1: List[int] = await provider7()  # type: ignore
 | 
			
		||||
    var2: List[int] = await provider7.async_()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test 8: to check the return type with async resource subclass
 | 
			
		||||
class MyResource8(resources.AsyncResource[List[int]]):
 | 
			
		||||
    async def init(self, *args, **kwargs) -> List[int]:
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
    async def shutdown(self, resource: List[int]) -> None:
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
provider8 = providers.Resource(MyResource8)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _provide8() -> None:
 | 
			
		||||
    var1: List[int] = await provider8()  # type: ignore
 | 
			
		||||
    var2: List[int] = await provider8.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from dependency_injector import providers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +9,7 @@ provider1 = providers.Selector(
 | 
			
		|||
    a=providers.Factory(object),
 | 
			
		||||
    b=providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
var1: int = provider1()
 | 
			
		||||
var1: Any = provider1()
 | 
			
		||||
 | 
			
		||||
# Test 2: to check the provided instance interface
 | 
			
		||||
provider2 = providers.Selector(
 | 
			
		||||
| 
						 | 
				
			
			@ -27,3 +29,13 @@ provider3 = providers.Selector(
 | 
			
		|||
    b=providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
attr3: providers.Provider = provider3.a
 | 
			
		||||
 | 
			
		||||
# Test 4: to check the return type with await
 | 
			
		||||
provider4 = providers.Selector(
 | 
			
		||||
    lambda: 'a',
 | 
			
		||||
    a=providers.Factory(object),
 | 
			
		||||
    b=providers.Factory(object),
 | 
			
		||||
)
 | 
			
		||||
async def _async4() -> None:
 | 
			
		||||
    var1: Any = await provider4()  # type: ignore
 | 
			
		||||
    var2: Any = await provider4.async_()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,3 +69,9 @@ animal11: Animal = provider11(1, 2, 3, b='1', c=2, e=0.0)
 | 
			
		|||
 | 
			
		||||
# Test 12: to check the SingletonDelegate __init__
 | 
			
		||||
provider12 = providers.SingletonDelegate(providers.Singleton(object))
 | 
			
		||||
 | 
			
		||||
# Test 13: to check the return type with await
 | 
			
		||||
provider13 = providers.Singleton(Cat)
 | 
			
		||||
async def _async13() -> None:
 | 
			
		||||
    animal1: Animal = await provider13(1, 2, 3, b='1', c=2, e=0.0)  # type: ignore
 | 
			
		||||
    animal2: Animal = await provider13.async_(1, 2, 3, b='1', c=2, e=0.0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								tests/unit/asyncutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tests/unit/asyncutils.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
"""Test utils."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import contextlib
 | 
			
		||||
import sys
 | 
			
		||||
import gc
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run(main):
 | 
			
		||||
    loop = asyncio.get_event_loop()
 | 
			
		||||
    return loop.run_until_complete(main)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_test_loop(
 | 
			
		||||
        loop_factory=asyncio.new_event_loop
 | 
			
		||||
) -> asyncio.AbstractEventLoop:
 | 
			
		||||
    loop = loop_factory()
 | 
			
		||||
    try:
 | 
			
		||||
        module = loop.__class__.__module__
 | 
			
		||||
        skip_watcher = 'uvloop' in module
 | 
			
		||||
    except AttributeError:  # pragma: no cover
 | 
			
		||||
        # Just in case
 | 
			
		||||
        skip_watcher = True
 | 
			
		||||
    asyncio.set_event_loop(loop)
 | 
			
		||||
    if sys.platform != 'win32' and not skip_watcher:
 | 
			
		||||
        policy = asyncio.get_event_loop_policy()
 | 
			
		||||
        watcher = asyncio.SafeChildWatcher()  # type: ignore
 | 
			
		||||
        watcher.attach_loop(loop)
 | 
			
		||||
        with contextlib.suppress(NotImplementedError):
 | 
			
		||||
            policy.set_child_watcher(watcher)
 | 
			
		||||
    return loop
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None:
 | 
			
		||||
    closed = loop.is_closed()
 | 
			
		||||
    if not closed:
 | 
			
		||||
        loop.call_soon(loop.stop)
 | 
			
		||||
        loop.run_forever()
 | 
			
		||||
        loop.close()
 | 
			
		||||
 | 
			
		||||
    if not fast:
 | 
			
		||||
        gc.collect()
 | 
			
		||||
 | 
			
		||||
    asyncio.set_event_loop(None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncTestCase(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.loop = setup_test_loop()
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        teardown_test_loop(self.loop)
 | 
			
		||||
 | 
			
		||||
    def _run(self, f):
 | 
			
		||||
        return self.loop.run_until_complete(f)
 | 
			
		||||
							
								
								
									
										71
									
								
								tests/unit/containers/test_dynamic_async_resources_py36.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								tests/unit/containers/test_dynamic_async_resources_py36.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
"""Dependency injector dynamic container unit tests for async resources."""
 | 
			
		||||
 | 
			
		||||
import unittest2 as unittest
 | 
			
		||||
 | 
			
		||||
# Runtime import to get asyncutils module
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
from dependency_injector import (
 | 
			
		||||
    containers,
 | 
			
		||||
    providers,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncResourcesTest(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    @unittest.skipIf(sys.version_info[:2] <= (3, 5), 'Async test')
 | 
			
		||||
    def test_async_init_resources(self):
 | 
			
		||||
        async def _init1():
 | 
			
		||||
            _init1.init_counter += 1
 | 
			
		||||
            yield
 | 
			
		||||
            _init1.shutdown_counter += 1
 | 
			
		||||
 | 
			
		||||
        _init1.init_counter = 0
 | 
			
		||||
        _init1.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
        async def _init2():
 | 
			
		||||
            _init2.init_counter += 1
 | 
			
		||||
            yield
 | 
			
		||||
            _init2.shutdown_counter += 1
 | 
			
		||||
 | 
			
		||||
        _init2.init_counter = 0
 | 
			
		||||
        _init2.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
        class Container(containers.DeclarativeContainer):
 | 
			
		||||
            resource1 = providers.Resource(_init1)
 | 
			
		||||
            resource2 = providers.Resource(_init2)
 | 
			
		||||
 | 
			
		||||
        container = Container()
 | 
			
		||||
        self.assertEqual(_init1.init_counter, 0)
 | 
			
		||||
        self.assertEqual(_init1.shutdown_counter, 0)
 | 
			
		||||
        self.assertEqual(_init2.init_counter, 0)
 | 
			
		||||
        self.assertEqual(_init2.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self._run(container.init_resources())
 | 
			
		||||
        self.assertEqual(_init1.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init1.shutdown_counter, 0)
 | 
			
		||||
        self.assertEqual(_init2.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init2.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self._run(container.shutdown_resources())
 | 
			
		||||
        self.assertEqual(_init1.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init1.shutdown_counter, 1)
 | 
			
		||||
        self.assertEqual(_init2.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init2.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(container.init_resources())
 | 
			
		||||
        self._run(container.shutdown_resources())
 | 
			
		||||
        self.assertEqual(_init1.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init1.shutdown_counter, 2)
 | 
			
		||||
        self.assertEqual(_init2.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init2.shutdown_counter, 2)
 | 
			
		||||
| 
						 | 
				
			
			@ -231,7 +231,3 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
 | 
			
		|||
        self.assertEqual(_init1.shutdown_counter, 2)
 | 
			
		||||
        self.assertEqual(_init2.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init2.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										818
									
								
								tests/unit/providers/test_async_py36.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										818
									
								
								tests/unit/providers/test_async_py36.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,818 @@
 | 
			
		|||
import asyncio
 | 
			
		||||
import random
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers, errors
 | 
			
		||||
 | 
			
		||||
# Runtime import to get asyncutils module
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RESOURCE1 = object()
 | 
			
		||||
RESOURCE2 = object()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def init_resource(resource):
 | 
			
		||||
    await asyncio.sleep(random.randint(1, 10) / 1000)
 | 
			
		||||
    yield resource
 | 
			
		||||
    await asyncio.sleep(random.randint(1, 10) / 1000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Client:
 | 
			
		||||
    def __init__(self, resource1: object, resource2: object) -> None:
 | 
			
		||||
        self.resource1 = resource1
 | 
			
		||||
        self.resource2 = resource2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Service:
 | 
			
		||||
    def __init__(self, client: Client) -> None:
 | 
			
		||||
        self.client = client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
    resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
    resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
 | 
			
		||||
 | 
			
		||||
    client = providers.Factory(
 | 
			
		||||
        Client,
 | 
			
		||||
        resource1=resource1,
 | 
			
		||||
        resource2=resource2,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    service = providers.Factory(
 | 
			
		||||
        Service,
 | 
			
		||||
        client=client,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FactoryTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_args_injection(self):
 | 
			
		||||
        class ContainerWithArgs(containers.DeclarativeContainer):
 | 
			
		||||
            resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
 | 
			
		||||
 | 
			
		||||
            client = providers.Factory(
 | 
			
		||||
                Client,
 | 
			
		||||
                resource1,
 | 
			
		||||
                resource2,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            service = providers.Factory(
 | 
			
		||||
                Service,
 | 
			
		||||
                client,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        container = ContainerWithArgs()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client())
 | 
			
		||||
        client2 = self._run(container.client())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service())
 | 
			
		||||
        service2 = self._run(container.service())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(service1.client, service2.client)
 | 
			
		||||
 | 
			
		||||
    def test_kwargs_injection(self):
 | 
			
		||||
        container = Container()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client())
 | 
			
		||||
        client2 = self._run(container.client())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service())
 | 
			
		||||
        service2 = self._run(container.service())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(service1.client, service2.client)
 | 
			
		||||
 | 
			
		||||
    def test_context_kwargs_injection(self):
 | 
			
		||||
        resource2_extra = object()
 | 
			
		||||
 | 
			
		||||
        container = Container()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client(resource2=resource2_extra))
 | 
			
		||||
        client2 = self._run(container.client(resource2=resource2_extra))
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, resource2_extra)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, resource2_extra)
 | 
			
		||||
 | 
			
		||||
    def test_args_kwargs_injection(self):
 | 
			
		||||
        class ContainerWithArgsAndKwArgs(containers.DeclarativeContainer):
 | 
			
		||||
            resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
 | 
			
		||||
 | 
			
		||||
            client = providers.Factory(
 | 
			
		||||
                Client,
 | 
			
		||||
                resource1,
 | 
			
		||||
                resource2=resource2,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            service = providers.Factory(
 | 
			
		||||
                Service,
 | 
			
		||||
                client=client,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        container = ContainerWithArgsAndKwArgs()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client())
 | 
			
		||||
        client2 = self._run(container.client())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service())
 | 
			
		||||
        service2 = self._run(container.service())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(service1.client, service2.client)
 | 
			
		||||
 | 
			
		||||
    def test_attributes_injection(self):
 | 
			
		||||
        class ContainerWithAttributes(containers.DeclarativeContainer):
 | 
			
		||||
            resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
 | 
			
		||||
 | 
			
		||||
            client = providers.Factory(
 | 
			
		||||
                Client,
 | 
			
		||||
                resource1,
 | 
			
		||||
                resource2=None,
 | 
			
		||||
            )
 | 
			
		||||
            client.add_attributes(resource2=resource2)
 | 
			
		||||
 | 
			
		||||
            service = providers.Factory(
 | 
			
		||||
                Service,
 | 
			
		||||
                client=None,
 | 
			
		||||
            )
 | 
			
		||||
            service.add_attributes(client=client)
 | 
			
		||||
 | 
			
		||||
        container = ContainerWithAttributes()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client())
 | 
			
		||||
        client2 = self._run(container.client())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service())
 | 
			
		||||
        service2 = self._run(container.service())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(service1.client, service2.client)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FactoryAggregateTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        object1 = object()
 | 
			
		||||
        object2 = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_object1():
 | 
			
		||||
            return object1
 | 
			
		||||
 | 
			
		||||
        def _get_object2():
 | 
			
		||||
            return object2
 | 
			
		||||
 | 
			
		||||
        provider = providers.FactoryAggregate(
 | 
			
		||||
            object1=providers.Factory(_get_object1),
 | 
			
		||||
            object2=providers.Factory(_get_object2),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        created_object1 = self._run(provider('object1'))
 | 
			
		||||
        self.assertIs(created_object1, object1)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        created_object2 = self._run(provider('object2'))
 | 
			
		||||
        self.assertIs(created_object2, object2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_injections(self):
 | 
			
		||||
        class ContainerWithSingletons(containers.DeclarativeContainer):
 | 
			
		||||
            resource1 = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            resource2 = providers.Resource(init_resource, providers.Object(RESOURCE2))
 | 
			
		||||
 | 
			
		||||
            client = providers.Singleton(
 | 
			
		||||
                Client,
 | 
			
		||||
                resource1=resource1,
 | 
			
		||||
                resource2=resource2,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            service = providers.Singleton(
 | 
			
		||||
                Service,
 | 
			
		||||
                client=client,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        container = ContainerWithSingletons()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client())
 | 
			
		||||
        client2 = self._run(container.client())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service())
 | 
			
		||||
        service2 = self._run(container.service())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(service1, service2)
 | 
			
		||||
        self.assertIs(service1.client, service2.client)
 | 
			
		||||
        self.assertIs(service1.client, client1)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(service2.client, client2)
 | 
			
		||||
        self.assertIs(client1, client2)
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.Singleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
    def test_async_init_with_error(self):
 | 
			
		||||
        # Disable default exception handling to prevent output
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(lambda loop, context: ...)
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            create_instance.counter += 1
 | 
			
		||||
            raise RuntimeError()
 | 
			
		||||
 | 
			
		||||
        create_instance.counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.Singleton(create_instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        future = provider()
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(RuntimeError):
 | 
			
		||||
            self._run(future)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(create_instance.counter, 1)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(RuntimeError):
 | 
			
		||||
            self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(create_instance.counter, 2)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        # Restore default exception handling
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedSingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.DelegatedSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ThreadSafeSingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.ThreadSafeSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedThreadSafeSingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.DelegatedThreadSafeSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ThreadLocalSingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.ThreadLocalSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_async_init_with_error(self):
 | 
			
		||||
        # Disable default exception handling to prevent output
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(lambda loop, context: ...)
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            create_instance.counter += 1
 | 
			
		||||
            raise RuntimeError()
 | 
			
		||||
        create_instance.counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.ThreadLocalSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        future = provider()
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(RuntimeError):
 | 
			
		||||
            self._run(future)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(create_instance.counter, 1)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(RuntimeError):
 | 
			
		||||
            self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(create_instance.counter, 2)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        # Restore default exception handling
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DelegatedThreadLocalSingletonTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        instance = object()
 | 
			
		||||
 | 
			
		||||
        async def create_instance():
 | 
			
		||||
            return instance
 | 
			
		||||
 | 
			
		||||
        provider = providers.DelegatedThreadLocalSingleton(create_instance)
 | 
			
		||||
 | 
			
		||||
        instance1 = self._run(provider())
 | 
			
		||||
        instance2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1, instance2)
 | 
			
		||||
        self.assertIs(instance, instance)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProvidedInstanceTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_provided_attribute(self):
 | 
			
		||||
        class TestClient:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
        class TestService:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
        class TestContainer(containers.DeclarativeContainer):
 | 
			
		||||
            resource = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            client = providers.Factory(TestClient, resource=resource)
 | 
			
		||||
            service = providers.Factory(TestService, resource=client.provided.resource)
 | 
			
		||||
 | 
			
		||||
        container = TestContainer()
 | 
			
		||||
 | 
			
		||||
        instance1, instance2 = self._run(
 | 
			
		||||
            asyncio.gather(
 | 
			
		||||
                container.service(),
 | 
			
		||||
                container.service(),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance2.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance1.resource, instance2.resource)
 | 
			
		||||
 | 
			
		||||
    def test_provided_item(self):
 | 
			
		||||
        class TestClient:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
            def __getitem__(self, item):
 | 
			
		||||
                return getattr(self, item)
 | 
			
		||||
 | 
			
		||||
        class TestService:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
        class TestContainer(containers.DeclarativeContainer):
 | 
			
		||||
            resource = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            client = providers.Factory(TestClient, resource=resource)
 | 
			
		||||
            service = providers.Factory(TestService, resource=client.provided['resource'])
 | 
			
		||||
 | 
			
		||||
        container = TestContainer()
 | 
			
		||||
 | 
			
		||||
        instance1, instance2 = self._run(
 | 
			
		||||
            asyncio.gather(
 | 
			
		||||
                container.service(),
 | 
			
		||||
                container.service(),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance2.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance1.resource, instance2.resource)
 | 
			
		||||
 | 
			
		||||
    def test_provided_method_call(self):
 | 
			
		||||
        class TestClient:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
            def get_resource(self):
 | 
			
		||||
                return self.resource
 | 
			
		||||
 | 
			
		||||
        class TestService:
 | 
			
		||||
            def __init__(self, resource):
 | 
			
		||||
                self.resource = resource
 | 
			
		||||
 | 
			
		||||
        class TestContainer(containers.DeclarativeContainer):
 | 
			
		||||
            resource = providers.Resource(init_resource, providers.Object(RESOURCE1))
 | 
			
		||||
            client = providers.Factory(TestClient, resource=resource)
 | 
			
		||||
            service = providers.Factory(TestService, resource=client.provided.get_resource.call())
 | 
			
		||||
 | 
			
		||||
        container = TestContainer()
 | 
			
		||||
 | 
			
		||||
        instance1, instance2 = self._run(
 | 
			
		||||
            asyncio.gather(
 | 
			
		||||
                container.service(),
 | 
			
		||||
                container.service(),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertIs(instance1.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance2.resource, RESOURCE1)
 | 
			
		||||
        self.assertIs(instance1.resource, instance2.resource)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DependencyTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_isinstance(self):
 | 
			
		||||
        dependency = 1.0
 | 
			
		||||
 | 
			
		||||
        async def get_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Dependency(instance_of=float)
 | 
			
		||||
        provider.override(providers.Callable(get_async))
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        dependency1 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        dependency2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(dependency1, dependency)
 | 
			
		||||
        self.assertEqual(dependency2, dependency)
 | 
			
		||||
 | 
			
		||||
    def test_isinstance_invalid(self):
 | 
			
		||||
        async def get_async():
 | 
			
		||||
            return {}
 | 
			
		||||
 | 
			
		||||
        provider = providers.Dependency(instance_of=float)
 | 
			
		||||
        provider.override(providers.Callable(get_async))
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(errors.Error):
 | 
			
		||||
            self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
    def test_async_mode(self):
 | 
			
		||||
        dependency = 123
 | 
			
		||||
 | 
			
		||||
        async def get_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        def get_sync():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Dependency(instance_of=int)
 | 
			
		||||
        provider.override(providers.Factory(get_async))
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        dependency1 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        dependency2 = self._run(provider())
 | 
			
		||||
        self.assertEqual(dependency1, dependency)
 | 
			
		||||
        self.assertEqual(dependency2, dependency)
 | 
			
		||||
 | 
			
		||||
        provider.override(providers.Factory(get_sync))
 | 
			
		||||
 | 
			
		||||
        dependency3 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        dependency4 = self._run(provider())
 | 
			
		||||
        self.assertEqual(dependency3, dependency)
 | 
			
		||||
        self.assertEqual(dependency4, dependency)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OverrideTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_provider(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_dependency_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        def _get_dependency_sync():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Provider()
 | 
			
		||||
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency_async))
 | 
			
		||||
        dependency1 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency_sync))
 | 
			
		||||
        dependency2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(dependency1, dependency)
 | 
			
		||||
        self.assertIs(dependency2, dependency)
 | 
			
		||||
 | 
			
		||||
    def test_callable(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_dependency_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        def _get_dependency_sync():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Callable(_get_dependency_async)
 | 
			
		||||
        dependency1 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency_sync))
 | 
			
		||||
        dependency2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(dependency1, dependency)
 | 
			
		||||
        self.assertIs(dependency2, dependency)
 | 
			
		||||
 | 
			
		||||
    def test_factory(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_dependency_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        def _get_dependency_sync():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Factory(_get_dependency_async)
 | 
			
		||||
        dependency1 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency_sync))
 | 
			
		||||
        dependency2 = self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(dependency1, dependency)
 | 
			
		||||
        self.assertIs(dependency2, dependency)
 | 
			
		||||
 | 
			
		||||
    def test_async_mode_enabling(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_dependency_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Callable(_get_dependency_async)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
    def test_async_mode_disabling(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        def _get_dependency():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Callable(_get_dependency)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        provider()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_disabled())
 | 
			
		||||
 | 
			
		||||
    def test_async_mode_enabling_on_overriding(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        async def _get_dependency_async():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Provider()
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency_async))
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        self._run(provider())
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
    def test_async_mode_disabling_on_overriding(self):
 | 
			
		||||
        dependency = object()
 | 
			
		||||
 | 
			
		||||
        def _get_dependency():
 | 
			
		||||
            return dependency
 | 
			
		||||
 | 
			
		||||
        provider = providers.Provider()
 | 
			
		||||
        provider.override(providers.Callable(_get_dependency))
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        provider()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_disabled())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestAsyncModeApi(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.provider = providers.Provider()
 | 
			
		||||
 | 
			
		||||
    def test_default_mode(self):
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_enabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_disabled())
 | 
			
		||||
        self.assertTrue(self.provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
    def test_enable(self):
 | 
			
		||||
        self.provider.enable_async_mode()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.provider.is_async_mode_enabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_disabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
    def test_disable(self):
 | 
			
		||||
        self.provider.disable_async_mode()
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_enabled())
 | 
			
		||||
        self.assertTrue(self.provider.is_async_mode_disabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
    def test_reset(self):
 | 
			
		||||
        self.provider.enable_async_mode()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(self.provider.is_async_mode_enabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_disabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
        self.provider.reset_async_mode()
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_enabled())
 | 
			
		||||
        self.assertFalse(self.provider.is_async_mode_disabled())
 | 
			
		||||
        self.assertTrue(self.provider.is_async_mode_undefined())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncTypingStubTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_(self):
 | 
			
		||||
        container = Container()
 | 
			
		||||
 | 
			
		||||
        client1 = self._run(container.client.async_())
 | 
			
		||||
        client2 = self._run(container.client.async_())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client1, Client)
 | 
			
		||||
        self.assertIs(client1.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client1.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(client2, Client)
 | 
			
		||||
        self.assertIs(client2.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(client2.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        service1 = self._run(container.service.async_())
 | 
			
		||||
        service2 = self._run(container.service.async_())
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service1, Service)
 | 
			
		||||
        self.assertIsInstance(service1.client, Client)
 | 
			
		||||
        self.assertIs(service1.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service1.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsInstance(service2, Service)
 | 
			
		||||
        self.assertIsInstance(service2.client, Client)
 | 
			
		||||
        self.assertIs(service2.client.resource1, RESOURCE1)
 | 
			
		||||
        self.assertIs(service2.client.resource2, RESOURCE2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(service1.client, service2.client)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +1,6 @@
 | 
			
		|||
"""Dependency injector coroutine providers unit tests."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import contextlib
 | 
			
		||||
import sys
 | 
			
		||||
import gc
 | 
			
		||||
 | 
			
		||||
import unittest2 as unittest
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +9,19 @@ from dependency_injector import (
 | 
			
		|||
    errors,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Runtime import to get asyncutils module
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _example(arg1, arg2, arg3, arg4):
 | 
			
		||||
    future = asyncio.Future()
 | 
			
		||||
| 
						 | 
				
			
			@ -25,52 +35,6 @@ def run(main):
 | 
			
		|||
    return loop.run_until_complete(main)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_test_loop(
 | 
			
		||||
        loop_factory=asyncio.new_event_loop
 | 
			
		||||
) -> asyncio.AbstractEventLoop:
 | 
			
		||||
    loop = loop_factory()
 | 
			
		||||
    try:
 | 
			
		||||
        module = loop.__class__.__module__
 | 
			
		||||
        skip_watcher = 'uvloop' in module
 | 
			
		||||
    except AttributeError:  # pragma: no cover
 | 
			
		||||
        # Just in case
 | 
			
		||||
        skip_watcher = True
 | 
			
		||||
    asyncio.set_event_loop(loop)
 | 
			
		||||
    if sys.platform != "win32" and not skip_watcher:
 | 
			
		||||
        policy = asyncio.get_event_loop_policy()
 | 
			
		||||
        watcher = asyncio.SafeChildWatcher()  # type: ignore
 | 
			
		||||
        watcher.attach_loop(loop)
 | 
			
		||||
        with contextlib.suppress(NotImplementedError):
 | 
			
		||||
            policy.set_child_watcher(watcher)
 | 
			
		||||
    return loop
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def teardown_test_loop(loop: asyncio.AbstractEventLoop,
 | 
			
		||||
                       fast: bool=False) -> None:
 | 
			
		||||
    closed = loop.is_closed()
 | 
			
		||||
    if not closed:
 | 
			
		||||
        loop.call_soon(loop.stop)
 | 
			
		||||
        loop.run_forever()
 | 
			
		||||
        loop.close()
 | 
			
		||||
 | 
			
		||||
    if not fast:
 | 
			
		||||
        gc.collect()
 | 
			
		||||
 | 
			
		||||
    asyncio.set_event_loop(None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncTestCase(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.loop = setup_test_loop()
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        teardown_test_loop(self.loop)
 | 
			
		||||
 | 
			
		||||
    def _run(self, f):
 | 
			
		||||
        return self.loop.run_until_complete(f)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CoroutineTests(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_init_with_coroutine(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -520,6 +520,24 @@ class FactoryAggregateTests(unittest.TestCase):
 | 
			
		|||
        self.assertEqual(object_b.init_arg3, 33)
 | 
			
		||||
        self.assertEqual(object_b.init_arg4, 44)
 | 
			
		||||
 | 
			
		||||
    def test_call_factory_name_as_kwarg(self):
 | 
			
		||||
        object_a = self.factory_aggregate(
 | 
			
		||||
            factory_name='example_a',
 | 
			
		||||
            init_arg1=1,
 | 
			
		||||
            init_arg2=2,
 | 
			
		||||
            init_arg3=3,
 | 
			
		||||
            init_arg4=4,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIsInstance(object_a, self.ExampleA)
 | 
			
		||||
        self.assertEqual(object_a.init_arg1, 1)
 | 
			
		||||
        self.assertEqual(object_a.init_arg2, 2)
 | 
			
		||||
        self.assertEqual(object_a.init_arg3, 3)
 | 
			
		||||
        self.assertEqual(object_a.init_arg4, 4)
 | 
			
		||||
 | 
			
		||||
    def test_call_no_factory_name(self):
 | 
			
		||||
        with self.assertRaises(TypeError):
 | 
			
		||||
            self.factory_aggregate()
 | 
			
		||||
 | 
			
		||||
    def test_call_no_such_provider(self):
 | 
			
		||||
        with self.assertRaises(errors.NoSuchProviderError):
 | 
			
		||||
            self.factory_aggregate('unknown')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,24 @@
 | 
			
		|||
"""Dependency injector resource provider unit tests."""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import asyncio
 | 
			
		||||
 | 
			
		||||
import unittest2 as unittest
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers, resources, errors
 | 
			
		||||
 | 
			
		||||
# Runtime import to get asyncutils module
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init_fn(*args, **kwargs):
 | 
			
		||||
    return args, kwargs
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +169,15 @@ class ResourceTests(unittest.TestCase):
 | 
			
		|||
        self.assertEqual(_init.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
    def test_shutdown_of_not_initialized(self):
 | 
			
		||||
        def _init():
 | 
			
		||||
            yield
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        result = provider.shutdown()
 | 
			
		||||
        self.assertIsNone(result)
 | 
			
		||||
 | 
			
		||||
    def test_initialized(self):
 | 
			
		||||
        provider = providers.Resource(init_fn)
 | 
			
		||||
        self.assertFalse(provider.initialized)
 | 
			
		||||
| 
						 | 
				
			
			@ -320,3 +342,186 @@ class ResourceTests(unittest.TestCase):
 | 
			
		|||
                provider.initialized,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncResourceTest(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_init_async_function(self):
 | 
			
		||||
        resource = object()
 | 
			
		||||
 | 
			
		||||
        async def _init():
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.counter += 1
 | 
			
		||||
            return resource
 | 
			
		||||
        _init.counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        result1 = self._run(provider())
 | 
			
		||||
        self.assertIs(result1, resource)
 | 
			
		||||
        self.assertEqual(_init.counter, 1)
 | 
			
		||||
 | 
			
		||||
        result2 = self._run(provider())
 | 
			
		||||
        self.assertIs(result2, resource)
 | 
			
		||||
        self.assertEqual(_init.counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
 | 
			
		||||
    def test_init_async_generator(self):
 | 
			
		||||
        resource = object()
 | 
			
		||||
 | 
			
		||||
        async def _init():
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.init_counter += 1
 | 
			
		||||
 | 
			
		||||
            yield resource
 | 
			
		||||
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.shutdown_counter += 1
 | 
			
		||||
 | 
			
		||||
        _init.init_counter = 0
 | 
			
		||||
        _init.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        result1 = self._run(provider())
 | 
			
		||||
        self.assertIs(result1, resource)
 | 
			
		||||
        self.assertEqual(_init.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        result2 = self._run(provider())
 | 
			
		||||
        self.assertIs(result2, resource)
 | 
			
		||||
        self.assertEqual(_init.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
    def test_init_async_class(self):
 | 
			
		||||
        resource = object()
 | 
			
		||||
 | 
			
		||||
        class TestResource(resources.AsyncResource):
 | 
			
		||||
            init_counter = 0
 | 
			
		||||
            shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
            async def init(self):
 | 
			
		||||
                await asyncio.sleep(0.001)
 | 
			
		||||
                self.__class__.init_counter += 1
 | 
			
		||||
                return resource
 | 
			
		||||
 | 
			
		||||
            async def shutdown(self, resource_):
 | 
			
		||||
                await asyncio.sleep(0.001)
 | 
			
		||||
                self.__class__.shutdown_counter += 1
 | 
			
		||||
                assert resource_ is resource
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(TestResource)
 | 
			
		||||
 | 
			
		||||
        result1 = self._run(provider())
 | 
			
		||||
        self.assertIs(result1, resource)
 | 
			
		||||
        self.assertEqual(TestResource.init_counter, 1)
 | 
			
		||||
        self.assertEqual(TestResource.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(TestResource.init_counter, 1)
 | 
			
		||||
        self.assertEqual(TestResource.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        result2 = self._run(provider())
 | 
			
		||||
        self.assertIs(result2, resource)
 | 
			
		||||
        self.assertEqual(TestResource.init_counter, 2)
 | 
			
		||||
        self.assertEqual(TestResource.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(TestResource.init_counter, 2)
 | 
			
		||||
        self.assertEqual(TestResource.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
    def test_init_with_error(self):
 | 
			
		||||
        async def _init():
 | 
			
		||||
            raise RuntimeError()
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        future = provider()
 | 
			
		||||
        self.assertTrue(provider.initialized)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
        # Disable default exception handling to prevent output
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(lambda loop, context: ...)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(RuntimeError):
 | 
			
		||||
            self._run(future)
 | 
			
		||||
 | 
			
		||||
        # Restore default exception handling
 | 
			
		||||
        asyncio.get_event_loop().set_exception_handler(None)
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(provider.initialized)
 | 
			
		||||
        self.assertTrue(provider.is_async_mode_enabled())
 | 
			
		||||
 | 
			
		||||
    def test_init_and_shutdown_methods(self):
 | 
			
		||||
        async def _init():
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.init_counter += 1
 | 
			
		||||
 | 
			
		||||
            yield
 | 
			
		||||
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.shutdown_counter += 1
 | 
			
		||||
 | 
			
		||||
        _init.init_counter = 0
 | 
			
		||||
        _init.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.init())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 1)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.init())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self._run(provider.shutdown())
 | 
			
		||||
        self.assertEqual(_init.init_counter, 2)
 | 
			
		||||
        self.assertEqual(_init.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
    def test_shutdown_of_not_initialized(self):
 | 
			
		||||
        async def _init():
 | 
			
		||||
            yield
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
        provider.enable_async_mode()
 | 
			
		||||
 | 
			
		||||
        result = self._run(provider.shutdown())
 | 
			
		||||
        self.assertIsNone(result)
 | 
			
		||||
 | 
			
		||||
    def test_concurrent_init(self):
 | 
			
		||||
        resource = object()
 | 
			
		||||
 | 
			
		||||
        async def _init():
 | 
			
		||||
            await asyncio.sleep(0.001)
 | 
			
		||||
            _init.counter += 1
 | 
			
		||||
            return resource
 | 
			
		||||
        _init.counter = 0
 | 
			
		||||
 | 
			
		||||
        provider = providers.Resource(_init)
 | 
			
		||||
 | 
			
		||||
        result1, result2 = self._run(
 | 
			
		||||
            asyncio.gather(
 | 
			
		||||
                provider(),
 | 
			
		||||
                provider()
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertIs(result1, resource)
 | 
			
		||||
        self.assertEqual(_init.counter, 1)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(result2, resource)
 | 
			
		||||
        self.assertEqual(_init.counter, 1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								tests/unit/samples/wiringsamples/asyncinjections.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								tests/unit/samples/wiringsamples/asyncinjections.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
import asyncio
 | 
			
		||||
 | 
			
		||||
from dependency_injector import containers, providers
 | 
			
		||||
from dependency_injector.wiring import inject, Provide, Closing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestResource:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.init_counter = 0
 | 
			
		||||
        self.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
    def reset_counters(self):
 | 
			
		||||
        self.init_counter = 0
 | 
			
		||||
        self.shutdown_counter = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
resource1 = TestResource()
 | 
			
		||||
resource2 = TestResource()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def async_resource(resource):
 | 
			
		||||
    await asyncio.sleep(0.001)
 | 
			
		||||
    resource.init_counter += 1
 | 
			
		||||
 | 
			
		||||
    yield resource
 | 
			
		||||
 | 
			
		||||
    await asyncio.sleep(0.001)
 | 
			
		||||
    resource.shutdown_counter += 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Container(containers.DeclarativeContainer):
 | 
			
		||||
 | 
			
		||||
    resource1 = providers.Resource(async_resource, providers.Object(resource1))
 | 
			
		||||
    resource2 = providers.Resource(async_resource, providers.Object(resource2))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@inject
 | 
			
		||||
async def async_injection(
 | 
			
		||||
        resource1: object = Provide[Container.resource1],
 | 
			
		||||
        resource2: object = Provide[Container.resource2],
 | 
			
		||||
):
 | 
			
		||||
    return resource1, resource2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@inject
 | 
			
		||||
async def async_injection_with_closing(
 | 
			
		||||
        resource1: object = Closing[Provide[Container.resource1]],
 | 
			
		||||
        resource2: object = Closing[Provide[Container.resource2]],
 | 
			
		||||
):
 | 
			
		||||
    return resource1, resource2
 | 
			
		||||
| 
						 | 
				
			
			@ -5,6 +5,12 @@ from dependency_injector.wiring import wire, Provide, Closing
 | 
			
		|||
 | 
			
		||||
# Runtime import to avoid syntax errors in samples on Python < 3.5
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
_SAMPLES_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
| 
						 | 
				
			
			@ -12,8 +18,11 @@ _SAMPLES_DIR = os.path.abspath(
 | 
			
		|||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
sys.path.append(_SAMPLES_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
from wiringsamples import module, package
 | 
			
		||||
from wiringsamples.service import Service
 | 
			
		||||
from wiringsamples.container import Container, SubContainer
 | 
			
		||||
| 
						 | 
				
			
			@ -267,3 +276,56 @@ class WiringAndFastAPITest(unittest.TestCase):
 | 
			
		|||
        self.assertEqual(result_2.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
        self.assertIsNot(result_1, result_2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WiringAsyncInjectionsTest(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_async_injections(self):
 | 
			
		||||
        from wiringsamples import asyncinjections
 | 
			
		||||
 | 
			
		||||
        container = asyncinjections.Container()
 | 
			
		||||
        container.wire(modules=[asyncinjections])
 | 
			
		||||
        self.addCleanup(container.unwire)
 | 
			
		||||
 | 
			
		||||
        asyncinjections.resource1.reset_counters()
 | 
			
		||||
        asyncinjections.resource2.reset_counters()
 | 
			
		||||
 | 
			
		||||
        resource1, resource2 = self._run(asyncinjections.async_injection())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource1, asyncinjections.resource1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.init_counter, 1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource2, asyncinjections.resource2)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.init_counter, 1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.shutdown_counter, 0)
 | 
			
		||||
 | 
			
		||||
    def test_async_injections_with_closing(self):
 | 
			
		||||
        from wiringsamples import asyncinjections
 | 
			
		||||
 | 
			
		||||
        container = asyncinjections.Container()
 | 
			
		||||
        container.wire(modules=[asyncinjections])
 | 
			
		||||
        self.addCleanup(container.unwire)
 | 
			
		||||
 | 
			
		||||
        asyncinjections.resource1.reset_counters()
 | 
			
		||||
        asyncinjections.resource2.reset_counters()
 | 
			
		||||
 | 
			
		||||
        resource1, resource2 = self._run(asyncinjections.async_injection_with_closing())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource1, asyncinjections.resource1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.init_counter, 1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource2, asyncinjections.resource2)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.init_counter, 1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.shutdown_counter, 1)
 | 
			
		||||
 | 
			
		||||
        resource1, resource2 = self._run(asyncinjections.async_injection_with_closing())
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource1, asyncinjections.resource1)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.init_counter, 2)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource1.shutdown_counter, 2)
 | 
			
		||||
 | 
			
		||||
        self.assertIs(resource2, asyncinjections.resource2)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.init_counter, 2)
 | 
			
		||||
        self.assertEqual(asyncinjections.resource2.shutdown_counter, 2)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,13 @@
 | 
			
		|||
import asyncio
 | 
			
		||||
import contextlib
 | 
			
		||||
import gc
 | 
			
		||||
import unittest
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
from httpx import AsyncClient
 | 
			
		||||
 | 
			
		||||
# Runtime import to avoid syntax errors in samples on Python < 3.5
 | 
			
		||||
# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir
 | 
			
		||||
import os
 | 
			
		||||
_TOP_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        '../',
 | 
			
		||||
    )),
 | 
			
		||||
)
 | 
			
		||||
_SAMPLES_DIR = os.path.abspath(
 | 
			
		||||
    os.path.sep.join((
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
| 
						 | 
				
			
			@ -15,58 +15,14 @@ _SAMPLES_DIR = os.path.abspath(
 | 
			
		|||
    )),
 | 
			
		||||
)
 | 
			
		||||
import sys
 | 
			
		||||
sys.path.append(_TOP_DIR)
 | 
			
		||||
sys.path.append(_SAMPLES_DIR)
 | 
			
		||||
 | 
			
		||||
from asyncutils import AsyncTestCase
 | 
			
		||||
 | 
			
		||||
from wiringfastapi import web
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# TODO: Refactor to use common async test case
 | 
			
		||||
def setup_test_loop(
 | 
			
		||||
        loop_factory=asyncio.new_event_loop
 | 
			
		||||
) -> asyncio.AbstractEventLoop:
 | 
			
		||||
    loop = loop_factory()
 | 
			
		||||
    try:
 | 
			
		||||
        module = loop.__class__.__module__
 | 
			
		||||
        skip_watcher = 'uvloop' in module
 | 
			
		||||
    except AttributeError:  # pragma: no cover
 | 
			
		||||
        # Just in case
 | 
			
		||||
        skip_watcher = True
 | 
			
		||||
    asyncio.set_event_loop(loop)
 | 
			
		||||
    if sys.platform != "win32" and not skip_watcher:
 | 
			
		||||
        policy = asyncio.get_event_loop_policy()
 | 
			
		||||
        watcher = asyncio.SafeChildWatcher()  # type: ignore
 | 
			
		||||
        watcher.attach_loop(loop)
 | 
			
		||||
        with contextlib.suppress(NotImplementedError):
 | 
			
		||||
            policy.set_child_watcher(watcher)
 | 
			
		||||
    return loop
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def teardown_test_loop(loop: asyncio.AbstractEventLoop,
 | 
			
		||||
                       fast: bool=False) -> None:
 | 
			
		||||
    closed = loop.is_closed()
 | 
			
		||||
    if not closed:
 | 
			
		||||
        loop.call_soon(loop.stop)
 | 
			
		||||
        loop.run_forever()
 | 
			
		||||
        loop.close()
 | 
			
		||||
 | 
			
		||||
    if not fast:
 | 
			
		||||
        gc.collect()
 | 
			
		||||
 | 
			
		||||
    asyncio.set_event_loop(None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AsyncTestCase(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.loop = setup_test_loop()
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        teardown_test_loop(self.loop)
 | 
			
		||||
 | 
			
		||||
    def _run(self, f):
 | 
			
		||||
        return self.loop.run_until_complete(f)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class WiringFastAPITest(AsyncTestCase):
 | 
			
		||||
 | 
			
		||||
    client: AsyncClient
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user