Skip to content

Commit

Permalink
cotnainer info
Browse files Browse the repository at this point in the history
  • Loading branch information
Tishka17 committed Feb 14, 2024
1 parent db0c96b commit cab43af
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
100 changes: 100 additions & 0 deletions docs/container/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
Container
*******************

**Container** is an object you use to get your dependency.

Basic usage
======================


Container can be synchronous or asynchronous.

* *Async* container can use any type of dependency sources: both sync and async are supported. Sync methods are called directly and no executors are used, so avoid network I/O in synchrous functions
* *Sync* container can use only synchronous dependency sources.

To create a top level container you should call ``make_container`` (or ``make_async_container``) and use it as a context manager. Pass there one or more providers.

.. code-block:: python
from dishka import make_container
with make_container(provider) as container:
...
And async version correspondingly:

.. code-block:: python
from dishka import make_container
async with make_async_container(provider) as container:
...
If you have not provided your own *scopes* enum, then default one will be used. Root container is attached to the first scope: Scope.APP by default.

To enter the next scope you should call container as a function and enter context manager (same sync or async):

.. code-block:: python
with container() as nested_container:
pass
Container as needed for retrieving objects. To do it you need call ``get(DependencyType)`` (and ``await`` it for async container).
All retrieved dependencies are stored inside container of corresponding scope until you exit that scope. So, you if you call ``get`` multiple times you will receive the same instance. The rule is followed for indirect dependencies as well. Multiple dependencies of the same scope have their own cache.

.. code-block:: python
with make_container(provider) as container:
a = container.get(A)
a = container.get(A) # same instance
Whe you exit the scope, dependency cache is cleared. Finalization of dependencies is done if you used generator factories

Thread/task safety
==========================

You can have multiple containers of the same scope simultaneously (except the top level one) - it is safe while you do not have dependencies of previous scope.

For example, if you have declared ``SessionPool`` as an APP-scoped dependency and then you concurrently enter REQUEST scope. Once you request ``SessionPool`` for the first time (directly or for another dependency) you cannot gurantee that only one instance of that object is created.

To prevent such a condition you need to protect any session whose children can be used concurrently: to pass ``lock_factory`` when creating a container. Do not mix up threadin and asyncio locks: they are interchangeable, use the proper one.


.. code-block:: python
import threading
with make_container(provider, lock_factory=threading.Lock) as container:
with container(lock_factory=threading.Lock) as nested_container:
...
.. code-block:: python
import asyncio
async with make_async_container(provider, lock_factory=asyncio.Lock) as container:
async with container(lock_factory=asyncio.Lock) as nested_container:
...
.. note::
Do not worry, lock is set by default for top level (``Scope.APP``) container. So, if you are not using other scopes concurrently you do not need any changes. (E.g. if you are not using multiple ``Scope.ACTION`` containers at a same time within one ``Scope.REQUEST`` container)

Context data
====================

Often, you scopes are assigned with some external events: HTTP-requests, message from queue, callbacks from framework. You can use those objects when creating dependencies. The difference from normal factories is that they are not created inside some ``Provder``, but passed to the scope:

.. code-block:: python
from framework import Request
class MyProvider:
@provide(scope=Scope.REQUEST)
def a(self, request: Request) -> A:
return A(data=request.contents)
while True:
request = connection.recv()
with container(context={Request:request}) as request_container:
a = request_container.get(A)
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dishka
concepts
requirements/technical
provider/index
container/index
integrations/index

.. toctree::
Expand Down

0 comments on commit cab43af

Please sign in to comment.