From 511834034d9df138a4e75a92fd8c7facdcb1c582 Mon Sep 17 00:00:00 2001 From: federinik Date: Wed, 21 Dec 2022 19:14:06 +0100 Subject: [PATCH 1/9] Allow Closing to detect dependent resources passed as kwargs too #636 --- src/dependency_injector/wiring.py | 2 +- .../samples/wiringstringids/resourceclosing.py | 6 ++++++ tests/unit/wiring/string_ids/test_main_py36.py | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index b1f01622..f62b4f31 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -598,7 +598,7 @@ def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, pr return {} closing_deps = {} - for arg in provider.args: + for arg in [*provider.args, *provider.kwargs.values()]: if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"): continue diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index 6360e15c..a143ad1d 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -36,6 +36,7 @@ class Container(containers.DeclarativeContainer): service = providers.Resource(init_service) factory_service = providers.Factory(FactoryService, service) + factory_service_kwargs = providers.Factory(FactoryService, service=service) @inject @@ -46,3 +47,8 @@ def test_function(service: Service = Closing[Provide["service"]]): @inject def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]): return factory + + +@inject +def test_function_dependency_kwargs(factory: FactoryService = Closing[Provide["factory_service_kwargs"]]): + return factory diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index d4c49fe8..826ef80c 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -303,7 +303,20 @@ def test_closing_dependency_resource(): assert result_2.service.init_counter == 2 assert result_2.service.shutdown_counter == 2 - assert result_1 is not result_2 + +@mark.usefixtures("resourceclosing_container") +def test_closing_dependency_resource_kwargs(): + resourceclosing.Service.reset_counter() + + result_1 = resourceclosing.test_function_dependency_kwargs() + assert isinstance(result_1, resourceclosing.FactoryService) + assert result_1.service.init_counter == 1 + assert result_1.service.shutdown_counter == 1 + + result_2 = resourceclosing.test_function_dependency_kwargs() + assert isinstance(result_2, resourceclosing.FactoryService) + assert result_2.service.init_counter == 2 + assert result_2.service.shutdown_counter == 2 @mark.usefixtures("resourceclosing_container") From f3d7e682a03dbf67ff3f1bfdb1bbc46c9ef15acd Mon Sep 17 00:00:00 2001 From: federinik Date: Thu, 22 Dec 2022 11:04:56 +0100 Subject: [PATCH 2/9] Use of update operator (|=) as per https://peps.python.org/pep-0584/ --- src/dependency_injector/wiring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index f62b4f31..d112061a 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -605,7 +605,7 @@ def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, pr if not arg.args and isinstance(arg, providers.Resource): return {str(id(arg)): arg} else: - closing_deps += _locate_dependent_closing_args(arg) + closing_deps |= _locate_dependent_closing_args(arg) return closing_deps From 5cdf1142377455ac4a491326524b421328a19cfe Mon Sep 17 00:00:00 2001 From: jazzthief Date: Mon, 8 May 2023 17:55:01 +0200 Subject: [PATCH 3/9] Add tests for nested deps resolution --- .../samples/wiringstringids/resourceclosing.py | 13 +++++++++++++ tests/unit/wiring/string_ids/test_main_py36.py | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index 6360e15c..afee7f7b 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -25,6 +25,11 @@ def __init__(self, service: Service): self.service = service +class NestedService: + def __init__(self, factory_service: FactoryService): + self.factory_service = factory_service + + def init_service(): service = Service() service.init() @@ -36,6 +41,7 @@ class Container(containers.DeclarativeContainer): service = providers.Resource(init_service) factory_service = providers.Factory(FactoryService, service) + nested_service = providers.Factory(NestedService, factory_service) @inject @@ -46,3 +52,10 @@ def test_function(service: Service = Closing[Provide["service"]]): @inject def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]): return factory + + +@inject +def test_function_nested_dependency( + nested: NestedService = Closing[Provide["nested_service"]] +): + return nested diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index d4c49fe8..1507b8e7 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -306,6 +306,23 @@ def test_closing_dependency_resource(): assert result_1 is not result_2 +@mark.usefixtures("resourceclosing_container") +def test_closing_nested_dependency_resource(): + resourceclosing.Service.reset_counter() + + result_1 = resourceclosing.test_function_nested_dependency() + assert isinstance(result_1, resourceclosing.NestedService) + assert result_1.factory_service.service.init_counter == 1 + assert result_1.factory_service.service.shutdown_counter == 1 + + result_2 = resourceclosing.test_function_nested_dependency() + assert isinstance(result_2, resourceclosing.NestedService) + assert result_2.factory_service.service.init_counter == 2 + assert result_2.factory_service.service.shutdown_counter == 2 + + assert result_1 is not result_2 + + @mark.usefixtures("resourceclosing_container") def test_closing_resource_bypass_marker_injection(): resourceclosing.Service.reset_counter() From d00afd9dcc36c68b3385625e005364430f08bb7f Mon Sep 17 00:00:00 2001 From: jazzthief Date: Fri, 12 May 2023 10:32:42 +0200 Subject: [PATCH 4/9] Fix resolution --- src/dependency_injector/wiring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index b1f01622..2088484f 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -605,7 +605,7 @@ def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, pr if not arg.args and isinstance(arg, providers.Resource): return {str(id(arg)): arg} else: - closing_deps += _locate_dependent_closing_args(arg) + closing_deps |= _locate_dependent_closing_args(arg) return closing_deps From 027ccfa161d7dd4d560ac896a4c22723b783ebad Mon Sep 17 00:00:00 2001 From: jazzthief Date: Tue, 23 May 2023 15:11:07 +0200 Subject: [PATCH 5/9] Restructure conditionals, incorporate kwargs check --- src/dependency_injector/wiring.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dependency_injector/wiring.py b/src/dependency_injector/wiring.py index d112061a..62e2a755 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -601,11 +601,11 @@ def _locate_dependent_closing_args(provider: providers.Provider) -> Dict[str, pr for arg in [*provider.args, *provider.kwargs.values()]: if not isinstance(arg, providers.Provider) or not hasattr(arg, "args"): continue - - if not arg.args and isinstance(arg, providers.Resource): + if isinstance(arg, providers.Resource): return {str(id(arg)): arg} - else: + if arg.args or arg.kwargs: closing_deps |= _locate_dependent_closing_args(arg) + return closing_deps From 2dabfaaad3469296cf44da3ca56782ce3577a493 Mon Sep 17 00:00:00 2001 From: jazzthief Date: Tue, 23 May 2023 18:24:51 +0200 Subject: [PATCH 6/9] Add test container with element after resource --- .../wiringstringids/resourceclosing.py | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index a5df21a8..e2817cf3 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -2,9 +2,14 @@ from dependency_injector.wiring import inject, Provide, Closing +class Singleton: + pass + + class Service: init_counter: int = 0 shutdown_counter: int = 0 + dependency: Singleton = None @classmethod def reset_counter(cls): @@ -12,7 +17,9 @@ def reset_counter(cls): cls.shutdown_counter = 0 @classmethod - def init(cls): + def init(cls, dependency: Singleton = None): + if dependency: + cls.dependency = dependency cls.init_counter += 1 @classmethod @@ -37,11 +44,36 @@ def init_service(): service.shutdown() +def init_service_with_singleton(singleton: Singleton): + service = Service() + service.init(singleton) + yield service + service.shutdown() + + class Container(containers.DeclarativeContainer): service = providers.Resource(init_service) factory_service = providers.Factory(FactoryService, service) - factory_service_kwargs = providers.Factory(FactoryService, service=service) + factory_service_kwargs = providers.Factory( + FactoryService, + service=service + ) + nested_service = providers.Factory(NestedService, factory_service) + + +class ContainerSingleton(containers.DeclarativeContainer): + + singleton = providers.Resource(Singleton) + service = providers.Resource( + init_service_with_singleton, + singleton + ) + factory_service = providers.Factory(FactoryService, service) + factory_service_kwargs = providers.Factory( + FactoryService, + service=service + ) nested_service = providers.Factory(NestedService, factory_service) From 2ca44a0b34a32d3e4c7a1b8998e311b5eb88fa62 Mon Sep 17 00:00:00 2001 From: jazzthief Date: Tue, 23 May 2023 18:26:07 +0200 Subject: [PATCH 7/9] Use parametrized fixture for containers --- tests/unit/wiring/string_ids/test_main_py36.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index 6be84999..aed9364d 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -33,9 +33,9 @@ def subcontainer(): container.unwire() -@fixture -def resourceclosing_container(): - container = resourceclosing.Container() +@fixture(params=[resourceclosing.Container, resourceclosing.ContainerSingleton]) +def resourceclosing_container(request): + container = request.param() container.wire(modules=[resourceclosing]) yield container container.unwire() From 64942563b3296990a331ceb7df7bfdaf574869d3 Mon Sep 17 00:00:00 2001 From: jazzthief Date: Wed, 24 May 2023 15:11:29 +0200 Subject: [PATCH 8/9] Restore removed `inject` marker --- tests/unit/samples/wiringstringids/resourceclosing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index e2817cf3..6d9888ee 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -92,6 +92,7 @@ def test_function_dependency_kwargs(factory: FactoryService = Closing[Provide["f return factory +@inject def test_function_nested_dependency( nested: NestedService = Closing[Provide["nested_service"]] ): From c0131549b0a09e019821044a0e31b224a4c70816 Mon Sep 17 00:00:00 2001 From: jazzthief Date: Wed, 24 May 2023 15:46:06 +0200 Subject: [PATCH 9/9] Use `Singleton` provider; clean up formatting --- tests/unit/samples/wiringstringids/resourceclosing.py | 10 +++++++--- tests/unit/wiring/string_ids/test_main_py36.py | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/unit/samples/wiringstringids/resourceclosing.py b/tests/unit/samples/wiringstringids/resourceclosing.py index 6d9888ee..b692c488 100644 --- a/tests/unit/samples/wiringstringids/resourceclosing.py +++ b/tests/unit/samples/wiringstringids/resourceclosing.py @@ -64,7 +64,7 @@ class Container(containers.DeclarativeContainer): class ContainerSingleton(containers.DeclarativeContainer): - singleton = providers.Resource(Singleton) + singleton = providers.Singleton(Singleton) service = providers.Resource( init_service_with_singleton, singleton @@ -83,12 +83,16 @@ def test_function(service: Service = Closing[Provide["service"]]): @inject -def test_function_dependency(factory: FactoryService = Closing[Provide["factory_service"]]): +def test_function_dependency( + factory: FactoryService = Closing[Provide["factory_service"]] +): return factory @inject -def test_function_dependency_kwargs(factory: FactoryService = Closing[Provide["factory_service_kwargs"]]): +def test_function_dependency_kwargs( + factory: FactoryService = Closing[Provide["factory_service_kwargs"]] +): return factory diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index aed9364d..2861c3aa 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -33,7 +33,10 @@ def subcontainer(): container.unwire() -@fixture(params=[resourceclosing.Container, resourceclosing.ContainerSingleton]) +@fixture(params=[ + resourceclosing.Container, + resourceclosing.ContainerSingleton, +]) def resourceclosing_container(request): container = request.param() container.wire(modules=[resourceclosing])