From 40ce9b6780db2724084286a722a37e394ed8bf09 Mon Sep 17 00:00:00 2001 From: slyces Date: Tue, 7 Jan 2025 17:20:34 +0100 Subject: [PATCH] fixup! feat: provide a typer integration, highly inspired by the click one --- docs/integrations/index.rst | 3 +- noxfile.py | 2 + src/dishka/integrations/typer.py | 7 +-- tests/integrations/typer/__init__.py | 1 - tests/integrations/typer/test_typer.py | 62 +++++++++++++++++++------- 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/docs/integrations/index.rst b/docs/integrations/index.rst index fbe7d799..12cbf729 100644 --- a/docs/integrations/index.rst +++ b/docs/integrations/index.rst @@ -40,12 +40,11 @@ You can create custom integrations for your framework of choice. - :ref:`aiogram` - :ref:`arq` - :ref:`Click` - - :ref:`Typer` * - :ref:`grpcio` - :ref:`Aiogram_dialog` - :ref:`FastStream` - - + - :ref:`Typer` * - :ref:`Fastapi` - :ref:`pyTelegramBotAPI` diff --git a/noxfile.py b/noxfile.py index d2d1b7ba..884b582e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -58,6 +58,8 @@ def get_tests(self) -> str: IntegrationEnv("taskiq", "latest"), IntegrationEnv("telebot", "415"), IntegrationEnv("telebot", "latest"), + IntegrationEnv("typer", "0151"), + IntegrationEnv("typer", "latest"), ] diff --git a/src/dishka/integrations/typer.py b/src/dishka/integrations/typer.py index b250bfdd..a41edffb 100644 --- a/src/dishka/integrations/typer.py +++ b/src/dishka/integrations/typer.py @@ -1,4 +1,5 @@ """Integration for Typer https://typer.tiangolo.com""" + __all__ = [ "FromDishka", "inject", @@ -10,7 +11,6 @@ import typer from click import get_current_context -from typer.models import CommandInfo from dishka import Container, FromDishka from .base import is_dishka_injected, wrap_injection @@ -32,7 +32,9 @@ def inject(func: Callable[..., T]) -> Callable[..., T]: def _inject_commands(app: typer.Typer) -> None: for command in app.registered_commands: - if command.callback is not None and not is_dishka_injected(command.callback): + if command.callback is not None and not is_dishka_injected( + command.callback, + ): command.callback = inject(command.callback) for group in app.registered_groups: @@ -47,7 +49,6 @@ def setup_dishka( finalize_container: bool = True, auto_inject: bool = False, ) -> None: - @app.callback() def inject_dishka_container(context: typer.Context) -> None: context.meta[CONTAINER_NAME] = container diff --git a/tests/integrations/typer/__init__.py b/tests/integrations/typer/__init__.py index abdc364e..3b7c8011 100644 --- a/tests/integrations/typer/__init__.py +++ b/tests/integrations/typer/__init__.py @@ -1,4 +1,3 @@ import pytest pytest.importorskip("typer") - diff --git a/tests/integrations/typer/test_typer.py b/tests/integrations/typer/test_typer.py index 33a195c7..f0449e88 100644 --- a/tests/integrations/typer/test_typer.py +++ b/tests/integrations/typer/test_typer.py @@ -1,8 +1,9 @@ -from contextlib import contextmanager -from typing import Annotated, Any, Callable, ContextManager, Iterable, Iterator +from collections.abc import Callable, Iterator +from contextlib import AbstractContextManager, contextmanager +from typing import Annotated, Any -import typer import pytest +import typer from typer.testing import CliRunner from dishka import FromDishka, make_container @@ -15,12 +16,15 @@ AppProvider, ) - -AppFactory = Callable[[Callable[..., Any], Provider], ContextManager[typer.Typer]] +AppFactory = Callable[ + [Callable[..., Any], Provider], AbstractContextManager[typer.Typer], +] @contextmanager -def dishka_app(handler: Callable[..., Any], provider: Provider) -> Iterator[typer.Typer]: +def dishka_app( + handler: Callable[..., Any], provider: Provider, +) -> Iterator[typer.Typer]: app = typer.Typer() app.command(name="test")(inject(handler)) @@ -32,26 +36,40 @@ def dishka_app(handler: Callable[..., Any], provider: Provider) -> Iterator[type @contextmanager -def dishka_auto_app(handler: Callable[..., Any], provider: Provider) -> Iterator[typer.Typer]: +def dishka_auto_app( + handler: Callable[..., Any], provider: Provider, +) -> Iterator[typer.Typer]: app = typer.Typer() app.command(name="test")(handler) container = make_container(provider) - setup_dishka(container=container, app=app, finalize_container=False, auto_inject=True) + setup_dishka( + container=container, + app=app, + finalize_container=False, + auto_inject=True, + ) yield app container.close() @contextmanager -def dishka_nested_group_app(handler: Callable[..., Any], provider: Provider) -> Iterator[typer.Typer]: +def dishka_nested_group_app( + handler: Callable[..., Any], provider: Provider, +) -> Iterator[typer.Typer]: app = typer.Typer() group = typer.Typer() group.command(name="sub")(handler) app.add_typer(group, name="test") container = make_container(provider) - setup_dishka(container=container, app=app, finalize_container=False, auto_inject=True) + setup_dishka( + container=container, + app=app, + finalize_container=False, + auto_inject=True, + ) yield app container.close() @@ -71,7 +89,9 @@ def handle_with_app( dishka_auto_app, ], ) -def test_app_dependency(app_provider: AppProvider, app_factory: AppFactory) -> None: +def test_app_dependency( + app_provider: AppProvider, app_factory: AppFactory, +) -> None: runner = CliRunner() with app_factory(handle_with_app, app_provider) as command: result = runner.invoke(command, ["test"]) @@ -107,7 +127,9 @@ def handle_with_app_and_options( dishka_auto_app, ], ) -def test_app_dependency_with_option(app_provider: AppProvider, app_factory: AppFactory) -> None: +def test_app_dependency_with_option( + app_provider: AppProvider, app_factory: AppFactory, +) -> None: runner = CliRunner() with app_factory(handle_with_app_and_options, app_provider) as command: result = runner.invoke(command, ["test", "Wade"]) @@ -116,10 +138,18 @@ def test_app_dependency_with_option(app_provider: AppProvider, app_factory: AppF app_provider.request_released.assert_not_called() -def test_app_dependency_with_nested_groups_and_option(app_provider: AppProvider) -> None: +def test_app_dependency_with_nested_groups_and_option( + app_provider: AppProvider, +) -> None: runner = CliRunner() - with dishka_nested_group_app(handle_with_app_and_options, app_provider) as command: - result = runner.invoke(command, ["test", "sub", "Wade", "--surname", "Wilson"]) + with dishka_nested_group_app( + handle_with_app_and_options, app_provider, + ) as command: + result = runner.invoke( + command, ["test", "sub", "Wade", "--surname", "Wilson"], + ) assert result.exit_code == 0, result.stdout - app_provider.app_mock.assert_called_with(APP_DEP_VALUE, "Wade", "Wilson") + app_provider.app_mock.assert_called_with( + APP_DEP_VALUE, "Wade", "Wilson", + ) app_provider.request_released.assert_not_called()