diff --git a/docs/providers/async.rst b/docs/providers/async.rst index 44ebe8e4..61fe74b3 100644 --- a/docs/providers/async.rst +++ b/docs/providers/async.rst @@ -32,3 +32,71 @@ This causes a cascade effect: └──> 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()`` diff --git a/docs/providers/images/async_mode.png b/docs/providers/images/async_mode.png new file mode 100644 index 00000000..8c7382f1 Binary files /dev/null and b/docs/providers/images/async_mode.png differ diff --git a/examples/providers/async_overriding.py b/examples/providers/async_overriding.py new file mode 100644 index 00000000..fc3ce320 --- /dev/null +++ b/examples/providers/async_overriding.py @@ -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))