From 5f6777db19688917895697fc885d2d854778ba64 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Tue, 23 Feb 2021 10:20:04 -0500 Subject: [PATCH 1/9] Add tests for ``.as_float()`` modifier usage with wiring --- docs/main/changelog.rst | 4 ++++ tests/unit/samples/wiringsamples/module.py | 4 ++++ .../samples/wiringstringidssamples/module.py | 16 +++++++++++++++- tests/unit/wiring/test_wiring_py36.py | 4 ++++ tests/unit/wiring/test_wiring_string_ids_py36.py | 4 ++++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 3df4dcef..cf3df9b7 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,10 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +Development version +------------------- +- Add tests for ``.as_float()`` modifier usage with wiring. + 4.26.0 ------ - Add wiring by string id. diff --git a/tests/unit/samples/wiringsamples/module.py b/tests/unit/samples/wiringsamples/module.py index 01031d2d..4c02b553 100644 --- a/tests/unit/samples/wiringsamples/module.py +++ b/tests/unit/samples/wiringsamples/module.py @@ -44,19 +44,23 @@ def test_function_provider(service_provider: Callable[..., Service] = Provider[C @inject def test_config_value( value_int: int = Provide[Container.config.a.b.c.as_int()], + value_float: float = Provide[Container.config.a.b.c.as_float()], value_str: str = Provide[Container.config.a.b.c.as_(str)], value_decimal: Decimal = Provide[Container.config.a.b.c.as_(Decimal)], value_required: str = Provide[Container.config.a.b.c.required()], value_required_int: int = Provide[Container.config.a.b.c.required().as_int()], + value_required_float: float = Provide[Container.config.a.b.c.required().as_float()], value_required_str: str = Provide[Container.config.a.b.c.required().as_(str)], value_required_decimal: str = Provide[Container.config.a.b.c.required().as_(Decimal)], ): return ( value_int, + value_float, value_str, value_decimal, value_required, value_required_int, + value_required_float, value_required_str, value_required_decimal, ) diff --git a/tests/unit/samples/wiringstringidssamples/module.py b/tests/unit/samples/wiringstringidssamples/module.py index f83a0a27..019e290b 100644 --- a/tests/unit/samples/wiringstringidssamples/module.py +++ b/tests/unit/samples/wiringstringidssamples/module.py @@ -3,7 +3,17 @@ from decimal import Decimal from typing import Callable -from dependency_injector.wiring import inject, Provide, Provider, as_int, as_, required, invariant, provided +from dependency_injector.wiring import ( + inject, + Provide, + Provider, + as_int, + as_float, + as_, + required, + invariant, + provided, +) from .container import Container from .service import Service @@ -44,19 +54,23 @@ def test_function_provider(service_provider: Callable[..., Service] = Provider[' @inject def test_config_value( value_int: int = Provide['config.a.b.c', as_int()], + value_float: float = Provide['config.a.b.c', as_float()], value_str: str = Provide['config.a.b.c', as_(str)], value_decimal: Decimal = Provide['config.a.b.c', as_(Decimal)], value_required: str = Provide['config.a.b.c', required()], value_required_int: int = Provide['config.a.b.c', required().as_int()], + value_required_float: float = Provide['config.a.b.c', required().as_float()], value_required_str: str = Provide['config.a.b.c', required().as_(str)], value_required_decimal: str = Provide['config.a.b.c', required().as_(Decimal)], ): return ( value_int, + value_float, value_str, value_decimal, value_required, value_required_int, + value_required_float, value_required_str, value_required_decimal, ) diff --git a/tests/unit/wiring/test_wiring_py36.py b/tests/unit/wiring/test_wiring_py36.py index 064aed9b..fd08799b 100644 --- a/tests/unit/wiring/test_wiring_py36.py +++ b/tests/unit/wiring/test_wiring_py36.py @@ -120,19 +120,23 @@ class WiringTest(unittest.TestCase): def test_configuration_option(self): ( value_int, + value_float, value_str, value_decimal, value_required, value_required_int, + value_required_float, value_required_str, value_required_decimal, ) = module.test_config_value() self.assertEqual(value_int, 10) + self.assertEqual(value_float, 10.0) self.assertEqual(value_str, '10') self.assertEqual(value_decimal, Decimal(10)) self.assertEqual(value_required, 10) self.assertEqual(value_required_int, 10) + self.assertEqual(value_required_float, 10.0) self.assertEqual(value_required_str, '10') self.assertEqual(value_required_decimal, Decimal(10)) diff --git a/tests/unit/wiring/test_wiring_string_ids_py36.py b/tests/unit/wiring/test_wiring_string_ids_py36.py index b4d41b3b..0acc99ce 100644 --- a/tests/unit/wiring/test_wiring_string_ids_py36.py +++ b/tests/unit/wiring/test_wiring_string_ids_py36.py @@ -120,19 +120,23 @@ class WiringTest(unittest.TestCase): def test_configuration_option(self): ( value_int, + value_float, value_str, value_decimal, value_required, value_required_int, + value_required_float, value_required_str, value_required_decimal, ) = module.test_config_value() self.assertEqual(value_int, 10) + self.assertEqual(value_float, 10.0) self.assertEqual(value_str, '10') self.assertEqual(value_decimal, Decimal(10)) self.assertEqual(value_required, 10) self.assertEqual(value_required_int, 10) + self.assertEqual(value_required_float, 10.0) self.assertEqual(value_required_str, '10') self.assertEqual(value_required_decimal, Decimal(10)) From 43eb15ed65ed710d1ad09be1486d3cfa761ea097 Mon Sep 17 00:00:00 2001 From: Shubhendra Singh Chauhan Date: Thu, 25 Feb 2021 20:14:15 +0530 Subject: [PATCH 2/9] fix: code quality issues (#406) * Refactor unnecessary `else` / `elif` when `if` block has a `return` statement * Remove unused imports * Use literal syntax to create data structure * revert "remove unused import" --- src/dependency_injector/wiring.py | 11 +++++------ tests/unit/providers/test_callables_py2_py3.py | 2 +- tests/unit/providers/test_coroutines_py35.py | 2 +- tests/unit/providers/test_factories_py2_py3.py | 2 +- tests/unit/providers/test_list_py2_py3.py | 2 +- tests/unit/providers/test_resource_py35.py | 2 +- tests/unit/providers/test_singletons_py2_py3.py | 2 +- tests/unit/wiring/test_wiring_string_ids_py36.py | 7 +------ 8 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index c7debfdf..96d1e238 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -111,21 +111,20 @@ class ProvidersMap: ) -> Optional[providers.Provider]: if isinstance(provider, providers.Delegate): return self._resolve_delegate(provider) - elif isinstance(provider, ( + if isinstance(provider, ( providers.ProvidedInstance, providers.AttributeGetter, providers.ItemGetter, providers.MethodCaller, )): return self._resolve_provided_instance(provider) - elif isinstance(provider, providers.ConfigurationOption): + if isinstance(provider, providers.ConfigurationOption): return self._resolve_config_option(provider) - elif isinstance(provider, providers.TypedConfigurationOption): + if isinstance(provider, providers.TypedConfigurationOption): return self._resolve_config_option(provider.option, as_=provider.provides) - elif isinstance(provider, str): + if isinstance(provider, str): return self._resolve_string_id(provider, modifier) - else: - return self._resolve_provider(provider) + return self._resolve_provider(provider) def _resolve_string_id( self, diff --git a/tests/unit/providers/test_callables_py2_py3.py b/tests/unit/providers/test_callables_py2_py3.py index 0e0168fa..dcee8478 100644 --- a/tests/unit/providers/test_callables_py2_py3.py +++ b/tests/unit/providers/test_callables_py2_py3.py @@ -69,7 +69,7 @@ class CallableTests(unittest.TestCase): provider = providers.Callable(_example) \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_set_kwargs(self): provider = providers.Callable(_example) \ diff --git a/tests/unit/providers/test_coroutines_py35.py b/tests/unit/providers/test_coroutines_py35.py index a3697044..b0dbf491 100644 --- a/tests/unit/providers/test_coroutines_py35.py +++ b/tests/unit/providers/test_coroutines_py35.py @@ -87,7 +87,7 @@ class CoroutineTests(AsyncTestCase): provider = providers.Coroutine(_example) \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_set_kwargs(self): provider = providers.Coroutine(_example) \ diff --git a/tests/unit/providers/test_factories_py2_py3.py b/tests/unit/providers/test_factories_py2_py3.py index b6587859..edf7de07 100644 --- a/tests/unit/providers/test_factories_py2_py3.py +++ b/tests/unit/providers/test_factories_py2_py3.py @@ -228,7 +228,7 @@ class FactoryTests(unittest.TestCase): provider = providers.Factory(Example) \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_set_kwargs(self): provider = providers.Factory(Example) \ diff --git a/tests/unit/providers/test_list_py2_py3.py b/tests/unit/providers/test_list_py2_py3.py index d11222cf..e303745b 100644 --- a/tests/unit/providers/test_list_py2_py3.py +++ b/tests/unit/providers/test_list_py2_py3.py @@ -42,7 +42,7 @@ class ListTests(unittest.TestCase): provider = providers.List() \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_clear_args(self): provider = providers.List() \ diff --git a/tests/unit/providers/test_resource_py35.py b/tests/unit/providers/test_resource_py35.py index 4b818198..b0943968 100644 --- a/tests/unit/providers/test_resource_py35.py +++ b/tests/unit/providers/test_resource_py35.py @@ -203,7 +203,7 @@ class ResourceTests(unittest.TestCase): provider = providers.Resource(init_fn) \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_clear_args(self): provider = providers.Resource(init_fn) \ diff --git a/tests/unit/providers/test_singletons_py2_py3.py b/tests/unit/providers/test_singletons_py2_py3.py index 6cc4c3c3..4e36f926 100644 --- a/tests/unit/providers/test_singletons_py2_py3.py +++ b/tests/unit/providers/test_singletons_py2_py3.py @@ -190,7 +190,7 @@ class _BaseSingletonTestCase(object): provider = self.singleton_cls(Example) \ .add_args(1, 2) \ .set_args(3, 4) - self.assertEqual(provider.args, tuple([3, 4])) + self.assertEqual(provider.args, (3, 4)) def test_set_kwargs(self): provider = self.singleton_cls(Example) \ diff --git a/tests/unit/wiring/test_wiring_string_ids_py36.py b/tests/unit/wiring/test_wiring_string_ids_py36.py index 0acc99ce..42002372 100644 --- a/tests/unit/wiring/test_wiring_string_ids_py36.py +++ b/tests/unit/wiring/test_wiring_string_ids_py36.py @@ -1,15 +1,10 @@ -import contextlib from decimal import Decimal -import importlib import unittest from dependency_injector.wiring import ( wire, Provide, - Closing, - register_loader_containers, - unregister_loader_containers, -) + Closing) from dependency_injector import errors # Runtime import to avoid syntax errors in samples on Python < 3.5 From 95b0356edcea7c0f5f8a69377cd5166902ca6394 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 25 Feb 2021 09:51:33 -0500 Subject: [PATCH 3/9] Update changelog --- docs/main/changelog.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index cf3df9b7..5204dc35 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -10,6 +10,13 @@ follows `Semantic versioning`_ Development version ------------------- - Add tests for ``.as_float()`` modifier usage with wiring. +- Make refactoring of wiring module and tests. + See PR # `#406 `_. + Thanks to `@withshubh `_ for the contribution: + - Refactor unnecessary ``else`` / ``elif`` in ``wiring`` module when ``if`` block has a + return statement. + - Remove unused imports in tests. + - Use literal syntax to create data structure in tests. 4.26.0 ------ From 3cf14c139f702bea0355a955d5ce888aa8f0aeae Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 25 Feb 2021 09:52:24 -0500 Subject: [PATCH 4/9] Add @withshubh to the list of contributors --- CONTRIBUTORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 54b3d582..23e26315 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -15,3 +15,4 @@ Dependency Injector Contributors + RĂ¼diger Busche (JarnoRFB) + Dmitry Rassoshenko (rda-dev) + Fotis Koutoupas (kootoopas) ++ Shubhendra Singh Chauhan (withshubh) From 9788a1888f3ea1a2ba7f992a2153faa253261640 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 25 Feb 2021 10:51:40 -0500 Subject: [PATCH 5/9] Add ``boto3`` example --- README.rst | 1 + docs/examples/boto3.rst | 20 ++++++ docs/examples/index.rst | 1 + docs/introduction/di_in_python.rst | 1 + docs/main/changelog.rst | 1 + docs/wiring.rst | 1 + examples/miniapps/boto3-session/README.rst | 14 ++++ .../boto3-session/boto3_session_example.py | 72 +++++++++++++++++++ 8 files changed, 111 insertions(+) create mode 100644 docs/examples/boto3.rst create mode 100644 examples/miniapps/boto3-session/README.rst create mode 100644 examples/miniapps/boto3-session/boto3_session_example.py diff --git a/README.rst b/README.rst index 6eba4ce6..f6f27bbc 100644 --- a/README.rst +++ b/README.rst @@ -155,6 +155,7 @@ Choose one of the following: - `Application example (single container) `_ - `Application example (multiple containers) `_ - `Decoupled packages example (multiple containers) `_ +- `Boto3 example `_ - `Django example `_ - `Flask example `_ - `Aiohttp example `_ diff --git a/docs/examples/boto3.rst b/docs/examples/boto3.rst new file mode 100644 index 00000000..8dc4bb6b --- /dev/null +++ b/docs/examples/boto3.rst @@ -0,0 +1,20 @@ +.. _boto3-example: + +Boto3 example +============= + +.. meta:: + :keywords: Python,Dependency Injection,Boto3,AWS,Amazon Web Services,S3,SQS,Rout53,EC2,Lambda,Example + :description: This example demonstrates a usage of Boto3 AWS client and Dependency Injector. + + +This example shows how to use ``Dependency Injector`` with `Boto3 `_. + +The source code is available on the `Github `_. + +Listing of ``boto3_session_example.py``: + +.. literalinclude:: ../../examples/miniapps/boto3-session/boto3_session_example.py + :language: python + +.. disqus:: diff --git a/docs/examples/index.rst b/docs/examples/index.rst index 6ec55c23..93595380 100644 --- a/docs/examples/index.rst +++ b/docs/examples/index.rst @@ -13,6 +13,7 @@ Explore the examples to see the ``Dependency Injector`` in action. application-single-container application-multiple-containers decoupled-packages + boto3 django flask flask-blueprints diff --git a/docs/introduction/di_in_python.rst b/docs/introduction/di_in_python.rst index 23ba547e..f19455e5 100644 --- a/docs/introduction/di_in_python.rst +++ b/docs/introduction/di_in_python.rst @@ -281,6 +281,7 @@ Choose one of the following as a next step: - :ref:`application-single-container` - :ref:`application-multiple-containers` - :ref:`decoupled-packages` + - :ref:`boto3` - :ref:`django-example` - :ref:`flask-example` - :ref:`flask-blueprints-example` diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 5204dc35..2b9830f5 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,6 +9,7 @@ follows `Semantic versioning`_ Development version ------------------- +- Add ``boto3`` example. - Add tests for ``.as_float()`` modifier usage with wiring. - Make refactoring of wiring module and tests. See PR # `#406 `_. diff --git a/docs/wiring.rst b/docs/wiring.rst index 195f469e..d4e2d6a6 100644 --- a/docs/wiring.rst +++ b/docs/wiring.rst @@ -405,6 +405,7 @@ Take a look at other application examples: - :ref:`application-single-container` - :ref:`application-multiple-containers` - :ref:`decoupled-packages` +- :ref:`boto3` - :ref:`django-example` - :ref:`flask-example` - :ref:`flask-blueprints-example` diff --git a/examples/miniapps/boto3-session/README.rst b/examples/miniapps/boto3-session/README.rst new file mode 100644 index 00000000..4c66ef92 --- /dev/null +++ b/examples/miniapps/boto3-session/README.rst @@ -0,0 +1,14 @@ +Boto3 Session Example +===================== + +This is a `Boto3 `_ session + +`Dependency Injector `_ example. + +Run +--- + +To run the application do: + +.. code-block:: bash + + python boto3_session_example.py diff --git a/examples/miniapps/boto3-session/boto3_session_example.py b/examples/miniapps/boto3-session/boto3_session_example.py new file mode 100644 index 00000000..33277513 --- /dev/null +++ b/examples/miniapps/boto3-session/boto3_session_example.py @@ -0,0 +1,72 @@ +"""Boto3 session example.""" + +import boto3.session +from dependency_injector import containers, providers + + +class Service: + def __init__(self, s3_client, sqs_client): + self.s3_client = s3_client + self.sqs_client = sqs_client + + +class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + session = providers.Resource( + boto3.session.Session, + aws_access_key_id=config.aws_access_key_id, + aws_secret_access_key=config.aws_secret_access_key, + aws_session_token=config.aws_session_token, + ) + + s3_client = providers.Resource( + session.provided.client.call(), + service_name='s3', + ) + + sqs_client = providers.Resource( + providers.MethodCaller(session.provided.client), # Alternative syntax + service_name='sqs', + ) + + service1 = providers.Factory( + Service, + s3_client=s3_client, + sqs_client=sqs_client, + ) + + service2 = providers.Factory( + Service, + s3_client=session.provided.client.call(service_name='s3'), # Alternative inline syntax + sqs_client=session.provided.client.call(service_name='sqs'), # Alternative inline syntax + ) + + +def main(): + container = Container() + container.config.aws_access_key_id.from_env('AWS_ACCESS_KEY_ID') + container.config.aws_secret_access_key.from_env('AWS_SECRET_ACCESS_KEY') + container.config.aws_session_token.from_env('AWS_SESSION_TOKEN') + container.init_resources() + + s3_client = container.s3_client() + print(s3_client) + + sqs_client = container.sqs_client() + print(sqs_client) + + service1 = container.service1() + print(service1, service1.s3_client, service1.sqs_client) + assert service1.s3_client is s3_client + assert service1.sqs_client is sqs_client + + service2 = container.service1() + print(service2, service2.s3_client, service2.sqs_client) + assert service2.s3_client is s3_client + assert service2.sqs_client is sqs_client + + +if __name__ == '__main__': + main() From 4ac798014ab92b2120e1a5dad3983f87de7c6231 Mon Sep 17 00:00:00 2001 From: Shubhendra Singh Chauhan Date: Fri, 26 Feb 2021 03:27:34 +0530 Subject: [PATCH 6/9] Integration: DeepSource (#407) * Refactor unnecessary `else` / `elif` when `if` block has a `return` statement * Remove unused imports * Use literal syntax to create data structure * revert "remove unused import" * Create .deepsource.toml --- .deepsource.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000..52933f17 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,12 @@ +version = 1 + +test_patterns = ["tests/**/test_*.py"] + +exclude_patterns = ["docs/**"] + +[[analyzers]] +name = "python" +enabled = true + + [analyzers.meta] + runtime_version = "3.x.x" From 6763ad2934976edc26d864bc358f61396fbe38e8 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 25 Feb 2021 17:02:01 -0500 Subject: [PATCH 7/9] Update changelog --- docs/main/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 2b9830f5..bfc873e8 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -18,6 +18,7 @@ Development version return statement. - Remove unused imports in tests. - Use literal syntax to create data structure in tests. +- Add integration with a static analysis tool `DeepSource `_. 4.26.0 ------ From 73b8a4aac4735e4acbbad818d6d41a51966a5116 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Sat, 27 Feb 2021 09:45:49 -0500 Subject: [PATCH 8/9] Introduce wiring inspect filter (#412) * Introduce wiring inspect filter * Upgrade exclusion filter * Refactor wiring --- docs/main/changelog.rst | 7 ++- src/dependency_injector/wiring.py | 54 ++++++++++++++++++---- tests/unit/samples/wiringflask/web.py | 34 ++++++++++++++ tests/unit/wiring/test_wiringflask_py36.py | 33 +++++++++++++ 4 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 tests/unit/samples/wiringflask/web.py create mode 100644 tests/unit/wiring/test_wiringflask_py36.py diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index bfc873e8..80a6c133 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,13 +9,16 @@ follows `Semantic versioning`_ Development version ------------------- +- Introduce wiring inspect filter to filter out ``flask.request`` and other local proxy objects + from the inspection. + See issue: `#408 `_. + Many thanks to `@bvanfleet `_ for reporting the issue and + help in finding the root cause. - Add ``boto3`` example. - Add tests for ``.as_float()`` modifier usage with wiring. - Make refactoring of wiring module and tests. See PR # `#406 `_. Thanks to `@withshubh `_ for the contribution: - - Refactor unnecessary ``else`` / ``elif`` in ``wiring`` module when ``if`` block has a - return statement. - Remove unused imports in tests. - Use literal syntax to create data structure in tests. - Add integration with a static analysis tool `DeepSource `_. diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index 96d1e238..c5a591da 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -37,10 +37,21 @@ else: try: - from fastapi.params import Depends as FastAPIDepends - fastapi_installed = True + import fastapi.params except ImportError: - fastapi_installed = False + fastapi = None + + +try: + import starlette.requests +except ImportError: + starlette = None + + +try: + import werkzeug.local +except ImportError: + werkzeug = None from . import providers @@ -111,20 +122,21 @@ class ProvidersMap: ) -> Optional[providers.Provider]: if isinstance(provider, providers.Delegate): return self._resolve_delegate(provider) - if isinstance(provider, ( + elif isinstance(provider, ( providers.ProvidedInstance, providers.AttributeGetter, providers.ItemGetter, providers.MethodCaller, )): return self._resolve_provided_instance(provider) - if isinstance(provider, providers.ConfigurationOption): + elif isinstance(provider, providers.ConfigurationOption): return self._resolve_config_option(provider) - if isinstance(provider, providers.TypedConfigurationOption): + elif isinstance(provider, providers.TypedConfigurationOption): return self._resolve_config_option(provider.option, as_=provider.provides) - if isinstance(provider, str): + elif isinstance(provider, str): return self._resolve_string_id(provider, modifier) - return self._resolve_provider(provider) + else: + return self._resolve_provider(provider) def _resolve_string_id( self, @@ -247,6 +259,28 @@ class ProvidersMap: return providers_map +class InspectFilter: + + def is_excluded(self, instance: object) -> bool: + if self._is_werkzeug_local_proxy(instance): + return True + elif self._is_starlette_request_cls(instance): + return True + else: + return False + + def _is_werkzeug_local_proxy(self, instance: object) -> bool: + return werkzeug and isinstance(instance, werkzeug.local.LocalProxy) + + def _is_starlette_request_cls(self, instance: object) -> bool: + return starlette \ + and isinstance(instance, type) \ + and issubclass(instance, starlette.requests.Request) + + +inspect_filter = InspectFilter() + + def wire( # noqa: C901 container: Container, *, @@ -268,6 +302,8 @@ def wire( # noqa: C901 for module in modules: for name, member in inspect.getmembers(module): + if inspect_filter.is_excluded(member): + continue if inspect.isfunction(member): _patch_fn(module, name, member, providers_map) elif inspect.isclass(member): @@ -530,7 +566,7 @@ def _is_fastapi_default_arg_injection(injection, kwargs): def _is_fastapi_depends(param: Any) -> bool: - return fastapi_installed and isinstance(param, FastAPIDepends) + return fastapi and isinstance(param, fastapi.params.Depends) def _is_patched(fn): diff --git a/tests/unit/samples/wiringflask/web.py b/tests/unit/samples/wiringflask/web.py new file mode 100644 index 00000000..59b5d004 --- /dev/null +++ b/tests/unit/samples/wiringflask/web.py @@ -0,0 +1,34 @@ +import sys + +from flask import Flask, jsonify, request, current_app, session, g +from flask import _request_ctx_stack, _app_ctx_stack +from dependency_injector import containers, providers +from dependency_injector.wiring import inject, Provide + +# This is here for testing wiring bypasses these objects without crashing +request, current_app, session, g # noqa +_request_ctx_stack, _app_ctx_stack # noqa + + +class Service: + def process(self) -> str: + return 'Ok' + + +class Container(containers.DeclarativeContainer): + + service = providers.Factory(Service) + + +app = Flask(__name__) + + +@app.route('/') +@inject +def index(service: Service = Provide[Container.service]): + result = service.process() + return jsonify({'result': result}) + + +container = Container() +container.wire(modules=[sys.modules[__name__]]) diff --git a/tests/unit/wiring/test_wiringflask_py36.py b/tests/unit/wiring/test_wiringflask_py36.py new file mode 100644 index 00000000..1eaaa4d8 --- /dev/null +++ b/tests/unit/wiring/test_wiringflask_py36.py @@ -0,0 +1,33 @@ +import unittest + +# Runtime import to avoid syntax errors in samples on Python < 3.5 and reach top-dir +import os +_TOP_DIR = os.path.abspath( + os.path.sep.join(( + os.path.dirname(__file__), + '../', + )), +) +_SAMPLES_DIR = os.path.abspath( + os.path.sep.join(( + os.path.dirname(__file__), + '../samples/', + )), +) +import sys +sys.path.append(_TOP_DIR) +sys.path.append(_SAMPLES_DIR) + +from wiringflask import web + + +class WiringFlaskTest(unittest.TestCase): + + def test(self): + client = web.app.test_client() + + with web.app.app_context(): + response = client.get('/') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, b'{"result":"Ok"}\n') From 48392beff2a0661945539604a970de3302e59bc6 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Sat, 27 Feb 2021 09:46:58 -0500 Subject: [PATCH 9/9] Bump version to 4.27.0 --- docs/main/changelog.rst | 4 ++-- src/dependency_injector/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 80a6c133..b2143e90 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,8 +7,8 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ -Development version -------------------- +4.27.0 +------ - Introduce wiring inspect filter to filter out ``flask.request`` and other local proxy objects from the inspection. See issue: `#408 `_. diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 46c5e9d0..3f784e9b 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Top-level package.""" -__version__ = '4.26.0' +__version__ = '4.27.0' """Version number. :type: str