-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
101 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ dishka | |
concepts | ||
requirements/technical | ||
provider/index | ||
container/index | ||
integrations/index | ||
|
||
.. toctree:: | ||
|