Skip to content

Commit

Permalink
fixup! feat: provide a typer integration, highly inspired by the clic…
Browse files Browse the repository at this point in the history
…k one
  • Loading branch information
Slyces committed Jan 7, 2025
1 parent 308e6ec commit 40ce9b6
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 22 deletions.
3 changes: 1 addition & 2 deletions docs/integrations/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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<telebot>`
Expand Down
2 changes: 2 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def get_tests(self) -> str:
IntegrationEnv("taskiq", "latest"),
IntegrationEnv("telebot", "415"),
IntegrationEnv("telebot", "latest"),
IntegrationEnv("typer", "0151"),
IntegrationEnv("typer", "latest"),
]


Expand Down
7 changes: 4 additions & 3 deletions src/dishka/integrations/typer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Integration for Typer https://typer.tiangolo.com"""

__all__ = [
"FromDishka",
"inject",
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion tests/integrations/typer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import pytest

pytest.importorskip("typer")

62 changes: 46 additions & 16 deletions tests/integrations/typer/test_typer.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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))

Expand All @@ -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()
Expand All @@ -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"])
Expand Down Expand Up @@ -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"])
Expand All @@ -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()

0 comments on commit 40ce9b6

Please sign in to comment.