Skip to content

Commit

Permalink
Merge pull request #256 from reagento/feature/integrations_docs
Browse files Browse the repository at this point in the history
Integrations docs
  • Loading branch information
Tishka17 authored Oct 20, 2024
2 parents 451660b + 82a9a70 commit d19a10f
Show file tree
Hide file tree
Showing 29 changed files with 1,183 additions and 291 deletions.
7 changes: 7 additions & 0 deletions docs/integrations/_websockets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. include:: <isonum.txt>

For most cases we operate single events like HTTP-requests. In this case we operate only 2 scopes: ``APP`` and ``REQUEST``.
Websockets are different: for one application you have multiple connections (one per client) and each connection delivers multiple messages.
To support this we use additional scope: ``SESSION``:

``APP`` |rarr| ``SESSION`` |rarr| ``REQUEST``
20 changes: 20 additions & 0 deletions docs/integrations/adding_new.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.. _adding_new:

Adding new integrations
===========================

Though there are some integrations in library you are not limited to use them.

The main points are:

1. Find a way to pass a global container instance. Often it is attached to application instance or passed by a middleware.
2. Find a place to enter request scope and how to pass it to a handler. Usually, it is entered in a middleware and container is stored in some kind of request context.
3. Configure a decorator. The main option here is to provide a way for retrieving container. Often, need to modify handler signature adding additional parameters. It is also available.
4. Check if you can apply decorator automatically.

While writing middlewares and working with scopes is done by your custom code, we have a helper for creating ``@inject`` decorators - a ``wrap_injection`` function.

* ``container_getter`` is a function with two params ``(args, kwargs)`` which is called to get a container used to retrieve dependencies within scope.
* ``additional_params`` is a list of ``inspect.Parameter`` which should be added to handler signature.

For more details, check existing integrations.
74 changes: 74 additions & 0 deletions docs/integrations/aiogram.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
.. _aiogram:

aiogram
===========================================

Though it is not required, you can use dishka-aiogram integration. It features:

* automatic REQUEST scope management using middleware
* passing ``TelegramObject`` object as a context data to providers for telegram events (update object fields)
* automatic injection of dependencies into handler function

Only async handlers are supported.

How to use
****************

1. Import

.. code-block:: python
from dishka.integrations.aiogram import (
AiogramProvider,
FromDishka,
inject,
setup_dishka,
)
from dishka import make_async_container, Provider, provide, Scope
2. Create provider. You can use ``aiogram.types.TelegramObject`` as a factory parameter to access on REQUEST-scope

.. code-block:: python
class YourProvider(Provider):
@provide(scope=Scope.REQUEST)
def create_x(self, event: TelegramObject) -> X:
...
3. Mark those of your handlers parameters which are to be injected with ``FromDishka[]``

.. code-block:: python
@dp.message()
async def start(
message: Message,
gateway: FromDishka[Gateway],
):
3a. *(optional)* decorate them using ``@inject`` if you are not using auto-injection

.. code-block:: python
@dp.message()
@inject
async def start(
message: Message,
gateway: FromDishka[Gateway],
):
4. *(optional)* Use ``AiogramProvider()`` when creating container if you are going to use ``aiogram.types.TelegramObject`` in providers.

.. code-block:: python
container = make_async_container(YourProvider(), AiogramProvider())
6. Setup dishka integration. ``autoinject=True`` is required unless you explicitly use ``@inject`` decorator

.. code-block:: python
setup_dishka(container=container, router=dp, auto_inject=True)
43 changes: 43 additions & 0 deletions docs/integrations/aiogram_dialog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.. _aiogram_dialog:

aiogram-dialog
===========================================


Though it is not required, you can use dishka-aiogram_dialog integration. It allows you to inject object into aiogram-dialog handlers


How to use
****************

1. Setup :ref:`aiogram integration<aiogram>`

2. Import decorator

.. code-block:: python
from dishka.integrations.aiogram_dialog import inject
3. Mark those of your aiogram-dialog handlers and getters parameters which are to be injected with ``FromDishka[]`` decorate them using imported ``@inject`` decorator.

.. code-block:: python
@inject
async def getter(
a: FromDishka[RequestDep],
mock: FromDishka[Mock],
**kwargs,
):
...
@inject
async def on_click(
event,
widget,
manager,
a: FromDishka[RequestDep],
mock: FromDishka[Mock],
):
...
106 changes: 106 additions & 0 deletions docs/integrations/aiohttp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.. _aiohttp:

aiohttp
===========================================

Though it is not required, you can use dishka-aiohttp integration. It features:

* automatic REQUEST and SESSION scope management using middleware
* passing ``Request`` object as a context data to providers for both **Websockets** and **HTTP** requests
* automatic injection of dependencies into handler function


How to use
****************

1. Import

.. code-block:: python
from dishka.integrations.aiohttp import (
DISHKA_CONTAINER_KEY,
FromDishka,
inject,
setup_dishka,
AiohttpProvider,
)
from dishka import make_async_container, Provider, provide, Scope
2. Create provider. You can use ``aiohttp.web.Request`` as a factory parameter to access HTTP or Websocket request.
It is available on ``SESSION`` and ``REQUEST`` scopes.

.. code-block:: python
class YourProvider(Provider):
@provide(scope=Scope.REQUEST)
def create_x(self, request: Request) -> X:
...
3. Mark those of your handlers parameters which are to be injected with ``FromDishka[]``

.. code-block:: python
@router.get('/')
async def endpoint(
request: str, gateway: FromDishka[Gateway],
) -> Response:
...
3a. *(optional)* decorate them using ``@inject``

.. code-block:: python
@router.get('/')
@inject
async def endpoint(
request: str, gateway: FromDishka[Gateway],
) -> Response:
...
4. *(optional)* Use ``AiohttpProvider()`` when creating container if you are going to use ``aiohttp.web.Request`` in providers.

.. code-block:: python
container = make_async_container(YourProvider(), AiohttpProvider())
5. Setup dishka integration. ``autoinject=True`` is required unless you explicitly use ``@inject`` decorator

.. code-block:: python
setup_dishka(container=container, app=app, autoinject=True)
6. *(optional)* Close container on app termination

.. code-block:: python
async def on_shutdown(app: Application):
await app[DISHKA_CONTAINER_KEY].close()
app.on_shutdown.append(on_shutdown)
Websockets
**********************

.. include:: _websockets.rst

In aiohttp your view function is called once per connection and then you retrieve messages in loop.
So, ``inject`` decorator can be only used to retrieve SESSION-scoped objects.
To achieve REQUEST-scope you can enter in manually:

.. code-block:: python
@inject
async def get_with_request(
request: Request,
a: FromDishka[A], # some object with Scope.SESSION
container: FromDishka[AsyncContainer], # container for Scope.SESSION
) -> web.WebsocketResponse:
websocket = web.WebsocketResponse()
await websocket.prepare(request)
async for message in weboscket:
# enter the nested scope, which is Scope.REQUEST
async with container() as request_container:
b = await request_container.get(B) # object with Scope.REQUEST
42 changes: 42 additions & 0 deletions docs/integrations/arq.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.. _arq:

arq
================

Though it is not required, you can use dishka-taskiq integration. It features:

* automatic REQUEST scope management using middleware
* injection of dependencies into task handler function using decorator


How to use
****************

1. Import

.. code-block:: python
from dishka.integrations.arq import (
FromDishka,
inject,
setup_dishka,
)
2. Create provider and container as usual

3. Mark those of your handlers parameters which are to be injected with ``FromDishka[]`` and decorate them using ``@inject``

.. code-block:: python
@inject
async def get_content(
context: dict[Any, Any],
gateway: FromDishka[Gateway],
):
...
4. Setup dishka integration on your Worker class or directly on WorkerSettings

.. code-block:: python
setup_dishka(container=container, worker_settings=WorkerSettings)
47 changes: 47 additions & 0 deletions docs/integrations/click.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.. _click:

Click
=================================


Though it is not required, you can use dishka-click integration. It features automatic injection to command handlers
In contrast with other integrations there is no scope management.



How to use
****************

1. Import

.. code-block:: python
from dishka.integrations.click import setup_dishka, inject
2. Create container in group handler and setup it to click context. Pass ``auto_inject=True`` unless you want to use ``@inject`` decorator explicitly.

.. code-block:: python
@click.group()
@click.pass_context
def main(context: click.Context):
container = make_container(MyProvider())
setup_dishka(container=container, context=context, auto_inject=True)
3. Mark those of your command handlers parameters which are to be injected with ``FromDishka[]``

.. code-block:: python
@main.command(name="hello")
def hello(interactor: FromDishka[Interactor]):
...
3a. *(optional)* decorate them using ``@inject``

.. code-block:: python
@main.command(name="hello")
@inject
def hello(interactor: FromDishka[Interactor]):
...
Loading

0 comments on commit d19a10f

Please sign in to comment.