diff --git a/docs/examples/fastdepends.rst b/docs/examples/fastdepends.rst new file mode 100644 index 00000000..5dc64fc4 --- /dev/null +++ b/docs/examples/fastdepends.rst @@ -0,0 +1,48 @@ +.. _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 demonstrates how to use ``Dependency Injector`` with `FastDepends `_, a lightweight dependency injection framework inspired by FastAPI's dependency system, but without the web framework components. + +Basic Usage +----------- + +The integration between FastDepends and Dependency Injector is straightforward. Simply use Dependency Injector's ``Provide`` marker within FastDepends' ``Depends`` function: + +.. 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/docs/examples/index.rst b/docs/examples/index.rst index 93595380..b166ceae 100644 --- a/docs/examples/index.rst +++ b/docs/examples/index.rst @@ -22,5 +22,6 @@ Explore the examples to see the ``Dependency Injector`` in action. fastapi fastapi-redis fastapi-sqlalchemy + fastdepends .. disqus:: diff --git a/docs/wiring.rst b/docs/wiring.rst index 02f64c60..4e849015 100644 --- a/docs/wiring.rst +++ b/docs/wiring.rst @@ -662,5 +662,6 @@ Take a look at other application examples: - :ref:`fastapi-example` - :ref:`fastapi-redis-example` - :ref:`fastapi-sqlalchemy-example` +- :ref:`fastdepends-example` .. disqus:: 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..7311b76a 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -48,10 +48,33 @@ 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: + 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) try: @@ -65,8 +88,7 @@ def get_origin(tp): except ImportError: werkzeug = None - -from . import providers +from . import providers # noqa: E402 __all__ = ( "wire", @@ -592,14 +614,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 +738,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..4d2b3d61 --- /dev/null +++ b/tests/unit/samples/wiringfastdepends/sample.py @@ -0,0 +1,39 @@ +import sys + +from fast_depends import Depends +from typing_extensions import Annotated + +from dependency_injector import containers, providers +from dependency_injector.wiring import Provide, inject + + +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__]]) diff --git a/tests/unit/wiring/test_fastdepends.py b/tests/unit/wiring/test_fastdepends.py new file mode 100644 index 00000000..9c9e2ad6 --- /dev/null +++ b/tests/unit/wiring/test_fastdepends.py @@ -0,0 +1,9 @@ +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 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]