diff --git a/docs/examples/fastdepends.rst b/docs/examples/fastdepends.rst new file mode 100644 index 00000000..327ecfc7 --- /dev/null +++ b/docs/examples/fastdepends.rst @@ -0,0 +1,52 @@ +.. _fastdepends-example: + +FastDepends example +=================== + +.. meta:: + :keywords: Python,Dependency Injection,FastDepends,Example + :description: This example demonstrates a usage of the FastDepends and Dependency Injector. + + +This example shows how to use ``Dependency Injector`` with `FastDepends `_. + +Example code is available on `Github `_. + +Quick sample +------------ + +Just use it within ``Depends`` + +.. code-block:: python + + import sys + + from dependency_injector import containers, providers + from dependency_injector.wiring import inject, Provide + from fast_depends import Depends + + + class CoefficientService: + @staticmethod + def get_coefficient() -> float: + return 1.2 + + + class Container(containers.DeclarativeContainer): + service = providers.Factory(CoefficientService) + + + @inject + def apply_coefficient( + a: int, + coefficient_provider: CoefficientService = Depends(Provide[Container.service]), + ) -> float: + return a * coefficient_provider.get_coefficient() + + + container = Container() + container.wire(modules=[sys.modules[__name__]]) + + apply_coefficient(100) == 120.0 + + diff --git a/requirements-dev.txt b/requirements-dev.txt index 47e3ca42..408b9bb6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,5 +20,6 @@ scipy boto3 mypy_boto3_s3 typing_extensions +fast-depends -r requirements-ext.txt diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index b8534ee5..ccb2cdc9 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -47,11 +47,19 @@ def get_args(hint): def get_origin(tp): return None +MARKER_EXTRACTORS = [] try: - import fastapi.params + from fastapi.params import Depends as FastApiDepends except ImportError: - fastapi = None + pass +else: + def extract_marker_from_fastapi(param: Any) -> Any: + if isinstance(param, FastApiDepends): + return param.dependency + return None + + MARKER_EXTRACTORS.append(extract_marker_from_fastapi) try: @@ -66,6 +74,18 @@ def get_origin(tp): werkzeug = None +try: + from fast_depends.dependencies import Depends as FastDepends +except ImportError: + pass +else: + def extract_marker_from_fast_depends(param: Any) -> Any: + if isinstance(param, FastDepends): + return param.dependency + return None + + MARKER_EXTRACTORS.append(extract_marker_from_fast_depends) + from . import providers __all__ = ( @@ -592,14 +612,13 @@ def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]: else: marker = parameter.default - if not isinstance(marker, _Marker) and not _is_fastapi_depends(marker): - return None - - if _is_fastapi_depends(marker): - marker = marker.dependency + for marker_extractor in MARKER_EXTRACTORS: + if _marker := marker_extractor(marker): + marker = _marker + break - if not isinstance(marker, _Marker): - return None + if not isinstance(marker, _Marker): + return None return marker @@ -717,10 +736,6 @@ def _get_patched( return patched -def _is_fastapi_depends(param: Any) -> bool: - return fastapi and isinstance(param, fastapi.params.Depends) - - def _is_patched(fn) -> bool: return _patched_registry.has_callable(fn) diff --git a/tests/unit/samples/wiringfastdepends/sample.py b/tests/unit/samples/wiringfastdepends/sample.py new file mode 100644 index 00000000..28310052 --- /dev/null +++ b/tests/unit/samples/wiringfastdepends/sample.py @@ -0,0 +1,36 @@ +import sys + +from dependency_injector import containers, providers +from dependency_injector.wiring import inject, Provide +from fast_depends import Depends +from typing_extensions import Annotated + + +class CoefficientService: + @staticmethod + def get_coefficient() -> float: + return 1.2 + + +class Container(containers.DeclarativeContainer): + service = providers.Factory(CoefficientService) + + +@inject +def apply_coefficient( + a: int, + coefficient_provider: CoefficientService = Depends(Provide[Container.service]), +) -> float: + return a * coefficient_provider.get_coefficient() + + +@inject +def apply_coefficient_annotated( + a: int, + coefficient_provider: Annotated[CoefficientService, Depends(Provide[Container.service])], +) -> float: + return a * coefficient_provider.get_coefficient() + + +container = Container() +container.wire(modules=[sys.modules[__name__]]) \ No newline at end of file diff --git a/tests/unit/wiring/test_fastdepends.py b/tests/unit/wiring/test_fastdepends.py new file mode 100644 index 00000000..2c326143 --- /dev/null +++ b/tests/unit/wiring/test_fastdepends.py @@ -0,0 +1,11 @@ +from dependency_injector.wiring import inject, Provide + +from wiringfastdepends import sample + + +def test_apply_coefficient() -> None: + assert sample.apply_coefficient(100) == 120.0 + + +def test_apply_coefficient_annotated() -> None: + assert sample.apply_coefficient_annotated(100) == 120.0 \ No newline at end of file diff --git a/tox.ini b/tox.ini index b2c5e79f..cadccd84 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,7 @@ deps= mypy_boto3_s3 pydantic-settings werkzeug + fast-depends extras= yaml commands = pytest @@ -44,6 +45,7 @@ deps = boto3 mypy_boto3_s3 werkzeug + fast-depends commands = pytest -m pydantic [testenv:coveralls]