python-dependency-injector/docs/providers/async.rst
Roman Mogylatov feed916f46
Async resources and injections (#352)
* Add support of async injections into wiring

* Add support of async functions and async generators for resources

* Update resource provider typing stub for stutdown

* Add resource base class for async resources

* Fix tests

* Add tests for async injections in wiring @inject

* Refactor provider tests

* Add tests for async resources

* Rework async resources callbacks to .add_done_callback() style (fixes pypy3 issue)

* Add awaits into async resource class test

* Refactor FastAPI tests

* Implement async resources initialization in container

* Move container async resource tests to a separate module for Python 3.6+

* Fix init async resources in container on Python 2

* Add first dirty async injections implementation

* Fix isawaitable error

* Turm asyncio import to conditional for safer Py2 usage

* Refactor kwargs injections

* Implement positional injections, add tests and make refactoring

* Implement attribute injections and add tests

* Add singleton implementation + tests for all singleton types

* Implement injections in thread-local and thread-safe singleton providers

* Update .provided + fix resource concurent initialization issue

* Implement async mode for Dependency provider

* Add async mode for the provider

* Add overload for Factory typing

* Add typing stubs for async resource

* Refactor abstract* providers __call__()

* Add async mode API + tests

* Add typing stubs & tests for async mode API

* Add tests for async mode auto configuration

* Refactor Provider.__call__() to use async mode api

* Refactor Dependency provider to use async mode api

* Add tests for Dependency provider async mode

* Add support of async mode for FactoryAggregate provider + tests

* Refactor Singleton provider to use async mode api

* Refactor ThreadSafeSingleton provider to use async mode api

* Refactor ThreadLocalSingleton provider to use async mode api

* Finish Singleton refactoring to use async mode api

* Refactor Resource provider to use async mode api

* Add Provider.async_() method + tests

* Add typing stubs for async_() method + tests

* Refactor Singleton typing stubs to return singleton from argument methods

* Refactor provider typing stubs

* Improve resource typing stub

* Add tests for async context kwargs injections

* Fix typo in resource provider tests

* Cover shutdown of not initialized resource

* Add test to cover resource initialization with an error

* Fix Singleton and ThreadLocalSingleton to handle initialization errors

* Add FastAPI + Redis example

* Make cosmetic fixes to FastAPI + Redis example

* Add missing development requirements

* Update module docblock in fastapi + redis example

* Add FastAPI + Redis example docs

* Add references to FastAPI + Redis example

* Refactor resource docs

* Add asynchronous resources docs

* Refactor wiring docs

* Add async injections docs for wiring

* Add async injections page and update docs index, readme, and key features pages

* Add providers async injections example

* Add docs on provider async mode enabling

* Reword async provider docs

* Add provider async mode docs

* Add cross links to async docs

* Mute flake8 errors in async provider examples

* Update changelog

* Make cosmetic fix to containers.pyx
2021-01-10 19:26:15 -05:00

109 lines
3.6 KiB
ReStructuredText

.. _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`