diff --git a/docs/examples/fastdepends.rst b/docs/examples/fastdepends.rst new file mode 100644 index 00000000..d3ebcc81 --- /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__]]) + + assert 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..805345a6 --- /dev/null +++ b/tests/unit/samples/wiringfastdepends/sample.py @@ -0,0 +1,37 @@ +from dependency_injector.wiring import inject, Provide +from fast_depends import Depends + +# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir +import os + +_SAMPLES_DIR = os.path.abspath( + os.path.sep.join( + ( + os.path.dirname(__file__), + "../samples/", + ) + ), +) +import sys + +sys.path.append(_SAMPLES_DIR) + + +from wiringfastdepends.sample import CoefficientService, Container + + + +@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__]]) + + +def test_wire_positive() -> None: + assert apply_coefficient(100) == 120.0 \ 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..3457cf09 --- /dev/null +++ b/tests/unit/wiring/test_fastdepends.py @@ -0,0 +1,30 @@ + +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__]]) + +assert apply_coefficient(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]