From dc8c8a099dd1079a248205c6e383aabc606723dd Mon Sep 17 00:00:00 2001 From: Andrey Tikhonov <17@itishka.org> Date: Mon, 8 Apr 2024 23:41:42 +0200 Subject: [PATCH 1/3] pytest integration prototype --- pyproject.toml | 2 ++ src/dishka/integrations/pytest.py | 30 ++++++++++++++++++++++++ tests/integrations/pytest/__init__.py | 0 tests/integrations/pytest/test_pytest.py | 14 +++++++++++ 4 files changed, 46 insertions(+) create mode 100644 src/dishka/integrations/pytest.py create mode 100644 tests/integrations/pytest/__init__.py create mode 100644 tests/integrations/pytest/test_pytest.py diff --git a/pyproject.toml b/pyproject.toml index 9a06ec21..27dae05d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ classifiers = [ dependencies = [ 'exceptiongroup>=1.1.3; python_version<"3.11"', ] +[project.entry-points.pytest11] +dishka = "dishka.integrations.pytest" [project.urls] "Source" = "https://github.com/reagento/dishka" diff --git a/src/dishka/integrations/pytest.py b/src/dishka/integrations/pytest.py new file mode 100644 index 00000000..01297691 --- /dev/null +++ b/src/dishka/integrations/pytest.py @@ -0,0 +1,30 @@ +from inspect import signature +from typing import Any + +import pytest + +from dishka import Container +from .base import default_parse_dependency, get_type_hints + + +@pytest.fixture(autouse=True) +def _dishka_inject( + request: pytest.FixtureRequest, +): + dependencies = {} + parse_dependency = default_parse_dependency + hints = get_type_hints(request.function, include_extras=True) + func_signature = signature(request.function) + for name, param in func_signature.parameters.items(): + hint = hints.get(name, Any) + dep = parse_dependency(param, hint) + if dep is None: + continue + dependencies[name] = dep + + if dependencies: + container: Container = request.getfixturevalue("dishka_container") + for name, dep in dependencies.items(): + request.node.funcargs[name] = container.get( + dep.type_hint, component=dep.component, + ) diff --git a/tests/integrations/pytest/__init__.py b/tests/integrations/pytest/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integrations/pytest/test_pytest.py b/tests/integrations/pytest/test_pytest.py new file mode 100644 index 00000000..19baa91f --- /dev/null +++ b/tests/integrations/pytest/test_pytest.py @@ -0,0 +1,14 @@ +import pytest + +from dishka import make_container, Provider, Scope, FromDishka + + +@pytest.fixture(scope="session") +def dishka_container(): + provider = Provider(scope=Scope.APP) + provider.provide(source=lambda: 42, provides=int) + return make_container(provider) + + +def test_xxx(value: FromDishka[int]) -> None: + assert value == 42 From 57a804827a33a8c2350a0a13d9bc7c637e5d597c Mon Sep 17 00:00:00 2001 From: Andrey Tikhonov <17@itishka.org> Date: Tue, 30 Jul 2024 23:23:05 +0200 Subject: [PATCH 2/3] explicit pytest --- src/dishka/integrations/pytest.py | 52 ++++++++++++------------ tests/integrations/pytest/test_pytest.py | 23 ++++++++++- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/dishka/integrations/pytest.py b/src/dishka/integrations/pytest.py index 01297691..3666bdd0 100644 --- a/src/dishka/integrations/pytest.py +++ b/src/dishka/integrations/pytest.py @@ -1,30 +1,32 @@ -from inspect import signature +from inspect import Parameter from typing import Any import pytest from dishka import Container -from .base import default_parse_dependency, get_type_hints - - -@pytest.fixture(autouse=True) -def _dishka_inject( - request: pytest.FixtureRequest, -): - dependencies = {} - parse_dependency = default_parse_dependency - hints = get_type_hints(request.function, include_extras=True) - func_signature = signature(request.function) - for name, param in func_signature.parameters.items(): - hint = hints.get(name, Any) - dep = parse_dependency(param, hint) - if dep is None: - continue - dependencies[name] = dep - - if dependencies: - container: Container = request.getfixturevalue("dishka_container") - for name, dep in dependencies.items(): - request.node.funcargs[name] = container.get( - dep.type_hint, component=dep.component, - ) +from .base import wrap_injection + +CONTAINER_NAME = "dishka_container" + + +def dishka_fixture(name: str, cls: Any): + def temp_fixture(dishka_container): + return dishka_container.get(cls) + + temp_fixture.__name__ = name + return pytest.fixture(temp_fixture) + + +def inject(func): + additional_params = [Parameter( + name=CONTAINER_NAME, + annotation=Container, + kind=Parameter.KEYWORD_ONLY, + )] + return wrap_injection( + func=func, + remove_depends=True, + container_getter=lambda _, p: p[CONTAINER_NAME], + additional_params=additional_params, + is_async=False, + ) diff --git a/tests/integrations/pytest/test_pytest.py b/tests/integrations/pytest/test_pytest.py index 19baa91f..4397e37a 100644 --- a/tests/integrations/pytest/test_pytest.py +++ b/tests/integrations/pytest/test_pytest.py @@ -1,6 +1,7 @@ import pytest -from dishka import make_container, Provider, Scope, FromDishka +from dishka import FromDishka, Provider, Scope, make_container +from dishka.integrations.pytest import dishka_fixture, inject @pytest.fixture(scope="session") @@ -10,5 +11,23 @@ def dishka_container(): return make_container(provider) -def test_xxx(value: FromDishka[int]) -> None: +@inject +def test_inject_test(value: FromDishka[int]) -> None: assert value == 42 + + +@pytest.fixture() +@inject +def some_fixture(value: FromDishka[int]): + return value + + +def test_inject_fixture(some_fixture) -> None: + assert some_fixture == 42 + + +explicit_fixture = dishka_fixture("explicit_fixture", int) + + +def test_explicit_fixture(explicit_fixture) -> None: + assert explicit_fixture == 42 From a464983910346cc392c8e75dcd543e88b83edffb Mon Sep 17 00:00:00 2001 From: Andrey Tikhonov <17@itishka.org> Date: Tue, 10 Sep 2024 10:11:51 +0200 Subject: [PATCH 3/3] close container in test --- tests/integrations/pytest/test_pytest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integrations/pytest/test_pytest.py b/tests/integrations/pytest/test_pytest.py index 4397e37a..d4e5b9f2 100644 --- a/tests/integrations/pytest/test_pytest.py +++ b/tests/integrations/pytest/test_pytest.py @@ -8,7 +8,9 @@ def dishka_container(): provider = Provider(scope=Scope.APP) provider.provide(source=lambda: 42, provides=int) - return make_container(provider) + container = make_container(provider) + yield container + container.close() @inject