diff --git a/docs/code_snippets/dependency_injection_example_sample.py b/docs/code_snippets/dependency_injection_example_sample.py new file mode 100644 index 0000000..09b83b0 --- /dev/null +++ b/docs/code_snippets/dependency_injection_example_sample.py @@ -0,0 +1,12 @@ +from sqlite3 import Connection + +from argenta import Response, Router +from argenta.di import FromDishka + + +router = Router() + +@router.command('connect') +def connect_handler(response: Response, connection: FromDishka[Connection]): + connection.execute('...') + \ No newline at end of file diff --git a/docs/code_snippets/dependency_injection_example_sample2.py b/docs/code_snippets/dependency_injection_example_sample2.py new file mode 100644 index 0000000..b4e01ca --- /dev/null +++ b/docs/code_snippets/dependency_injection_example_sample2.py @@ -0,0 +1,13 @@ +import sqlite3 +from sqlite3 import Connection +from typing import Iterable + +from dishka import Provider, Scope, provide + + +class ConnectionProvider(Provider): + @provide(scope=Scope.REQUEST) + def new_connection(self) -> Iterable[Connection]: + conn = sqlite3.connect(":memory:") + yield conn + conn.close() \ No newline at end of file diff --git a/docs/code_snippets/dependency_injection_example_sample3.py b/docs/code_snippets/dependency_injection_example_sample3.py new file mode 100644 index 0000000..182df70 --- /dev/null +++ b/docs/code_snippets/dependency_injection_example_sample3.py @@ -0,0 +1,3 @@ +from argenta import Orchestrator + +orchestrator = Orchestrator(custom_providers=[ConnectionProvider()]) diff --git a/docs/code_snippets/dependency_injection_example_sample4.py b/docs/code_snippets/dependency_injection_example_sample4.py new file mode 100644 index 0000000..9a8a1ee --- /dev/null +++ b/docs/code_snippets/dependency_injection_example_sample4.py @@ -0,0 +1,11 @@ +from argenta import Response, Router +from argenta.orchestrator.argparser import ArgSpace +from argenta.di import FromDishka + + +router = Router() + +@router.command('info') +def connect_handler(response: Response, argspace: FromDishka[ArgSpace]): + print(argspace.get_by_name('type')) + \ No newline at end of file diff --git a/docs/root/dependency_injection.rst b/docs/root/dependency_injection.rst index 76e99c3..2417a3d 100644 --- a/docs/root/dependency_injection.rst +++ b/docs/root/dependency_injection.rst @@ -1,6 +1,61 @@ .. _root_dependency_injection: Внедрение зависимостей -**************************** +======================= -мда мда мда +Внедрение зависимостей (Dependency Injection, DI) — это паттерн проектирования, который помогает писать слабосвязанный, легко тестируемый и расширяемый код. Вместо того чтобы хендлеры сами создавали нужные им объекты (зависимости) они лишь объявляют о необходимости в их получении, а ``Argenta`` "внедряет" их в хендлеры в момент вызова. + +``Argenta`` использует популярную библиотеку ``dishka`` для реализации DI, что позволяет вам декларативно объявлять зависимости прямо в сигнатурах ваших хендлеров. +Более подробно про ``DI``, ``IoC``, ``API`` создания провайдеров и другое вы можете прочитать тут_. + +.. _тут : https://dishka.readthedocs.io/en/stable/di_intro.html + +Основная идея +------------- + +Представьте, что вашему хендлеру для работы нужен доступ к базе данных. Вместо того чтобы импортировать и инициализировать соединение внутри функции, вы просто объявляете его как аргумент с тайп-хинтом: + +.. note:: + ``argenta.di.FromDishka`` это алиас к ``dishka.FromDishka``, они полностью взаимозаменяемы. + +.. literalinclude:: ../code_snippets/dependency_injection_example_sample.py + :language: python + +``Argenta`` -> ``dishka`` зарезолвит тайпхинты и внедрит зависимость с возвращаемым типом ``Connection``, прежде чем использовать зависимость её нужно создать, для этого нужно создать соответствующий провайдер. + +.. literalinclude:: ../code_snippets/dependency_injection_example_sample2.py + :language: python + +После создания провайдера, его нужно зарегистрировать в оркестраторе. + +.. literalinclude:: ../code_snippets/dependency_injection_example_sample3.py + :language: python + +Как это работает? +----------------- + +В основе DI в Argente лежат **провайдеры** и **контейнер**. + +* **Провайдер (Provider)** — это "рецепт", который объясняет, как создавать и настраивать ту или иную зависимость (например, подключение к БД, API-клиент или любой другой сервис). +* **Контейнер (IoC Container)** — это "фабрика", которая хранит все рецепты (провайдеры) и по запросу создаёт и выдаёт готовые зависимости. + +Встроенные провайдеры +----------------------- + +``Argenta`` поставляется с предопределённым провайдером, который даёт доступ к важным системным зависимостям без какой-либо настройки. К примеру вы можете получить объект ``ArgSpace``, который представляет из себя распаршенные аргументы командной строки при запуске приложения. + +Краткий сэмпл кода, который получает объект ``ArgSpace`` и выводит в консоль аргумент с именем "type": + +.. literalinclude:: ../code_snippets/dependency_injection_example_sample4.py + :language: python + +Обмен данными между хендлерами +------------------------------ + +Помимо DI, хендлеры могут общаться друг с другом в контексте приложения через **объект контекста** (в ``Argenta`` эту роль выполняет объект ``Response``). + +Каждый хендлер может записывать данные, читать, обновлять и удалять. + +.. seealso:: + + Подробнее об этом можно прочитать в разделе :ref:`root_api_response`. diff --git a/src/argenta/orchestrator/argparser/__init__.py b/src/argenta/orchestrator/argparser/__init__.py index 6cdfa27..906c5dc 100644 --- a/src/argenta/orchestrator/argparser/__init__.py +++ b/src/argenta/orchestrator/argparser/__init__.py @@ -3,3 +3,4 @@ from argenta.orchestrator.argparser.arguments import \ from argenta.orchestrator.argparser.arguments import \ ValueArgument as ValueArgument from argenta.orchestrator.argparser.entity import ArgParser as ArgParser +from argenta.orchestrator.argparser.entity import ArgSpace as ArgSpace