From 2fd55c23b1ad1c3ecc80e65a8ee9d58e398edbe2 Mon Sep 17 00:00:00 2001 From: Pablo Valverde <92272697+pavalso@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:03:23 +0100 Subject: [PATCH 1/3] Add provided().call *args, **kwargs arguments #945 --- 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 6b13e05c..60ccc65a 100644 --- a/src/dependency_injector/wiring.py +++ b/src/dependency_injector/wiring.py @@ -959,8 +959,8 @@ def __getitem__(self, item) -> Self: self.segments.append((self.TYPE_ITEM, item)) return self - def call(self) -> Self: - self.segments.append((self.TYPE_CALL, None)) + def call(self, *args, **kwargs) -> Self: + self.segments.append((self.TYPE_CALL, (args, kwargs))) return self def modify( @@ -975,7 +975,7 @@ def modify( elif type_ == ProvidedInstance.TYPE_ITEM: provider = provider[value] elif type_ == ProvidedInstance.TYPE_CALL: - provider = provider.call() + provider = provider.call(*value[0], **value[1]) else: assert_never(type_) return provider From 8ae6540ecaaa203edd93d51318c2f0ae20f34cdd Mon Sep 17 00:00:00 2001 From: Pablo Valverde <92272697+pavalso@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:52:35 +0100 Subject: [PATCH 2/3] Add ServiceWithCallable and related tests for method calls with args and kwargs --- tests/unit/samples/wiring/container.py | 4 +++- tests/unit/samples/wiring/module.py | 21 +++++++++++++++++++ tests/unit/samples/wiring/service.py | 13 ++++++++++++ .../wiring/provider_ids/test_main_py36.py | 15 +++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/unit/samples/wiring/container.py b/tests/unit/samples/wiring/container.py index 95681dbf..69eb7115 100644 --- a/tests/unit/samples/wiring/container.py +++ b/tests/unit/samples/wiring/container.py @@ -1,6 +1,6 @@ from dependency_injector import containers, providers -from .service import Service +from .service import Service, ServiceWithCallable class SubContainer(containers.DeclarativeContainer): @@ -14,4 +14,6 @@ class Container(containers.DeclarativeContainer): service = providers.Factory(Service) + service_with_callable = providers.Factory(ServiceWithCallable) + sub = providers.Container(SubContainer) diff --git a/tests/unit/samples/wiring/module.py b/tests/unit/samples/wiring/module.py index 3925b295..dbec1207 100644 --- a/tests/unit/samples/wiring/module.py +++ b/tests/unit/samples/wiring/module.py @@ -100,6 +100,27 @@ def test_provided_instance(some_value: int = Provide[Container.service.provided. return some_value +@inject +def test_provided_instance_call_with_args( + some_value: int = Provide[Container.service_with_callable.provided.method_with_args.call(1, 2)] +): + return some_value + + +@inject +def test_provided_instance_call_with_kwargs( + some_value: dict = Provide[Container.service_with_callable.provided.method_with_kwargs.call(a=1, b=2)] +): + return some_value + + +@inject +def test_provided_instance_call_with_args_and_kwargs( + some_value: dict = Provide[Container.service_with_callable.provided.foo.process.call(1, 2, key="value")] +): + return some_value + + @inject def test_subcontainer_provider(some_value: int = Provide[Container.sub.int_object]): return some_value diff --git a/tests/unit/samples/wiring/service.py b/tests/unit/samples/wiring/service.py index 4151b94f..15e2990b 100644 --- a/tests/unit/samples/wiring/service.py +++ b/tests/unit/samples/wiring/service.py @@ -1,2 +1,15 @@ class Service: service_attr: int + + +class ServiceWithCallable: + def __init__(self): + self.foo = CallableDict({"bar": lambda: 10}) + self.method_with_args = lambda x, y: x + y + self.method_with_kwargs = lambda **kwargs: kwargs + + +class CallableDict(dict): + def __init__(self, data): + super().__init__(data) + self.process = lambda *args, **kwargs: {"args": args, "kwargs": kwargs} diff --git a/tests/unit/wiring/provider_ids/test_main_py36.py b/tests/unit/wiring/provider_ids/test_main_py36.py index a36e50e8..21b16881 100644 --- a/tests/unit/wiring/provider_ids/test_main_py36.py +++ b/tests/unit/wiring/provider_ids/test_main_py36.py @@ -188,6 +188,21 @@ class TestService: assert some_value == 10 +def test_provided_instance_call_with_args(): + some_value = module.test_provided_instance_call_with_args() + assert some_value == 3 + + +def test_provided_instance_call_with_kwargs(): + some_value = module.test_provided_instance_call_with_kwargs() + assert some_value == {"a": 1, "b": 2} + + +def test_provided_instance_call_with_args_and_kwargs(): + some_value = module.test_provided_instance_call_with_args_and_kwargs() + assert some_value == {"args": (1, 2), "kwargs": {"key": "value"}} + + def test_subcontainer(): some_value = module.test_subcontainer_provider() assert some_value == 1 From f0998448b7c7d34361aea3a22e6f5a5df1430c4f Mon Sep 17 00:00:00 2001 From: ZipFile Date: Sun, 22 Mar 2026 12:53:47 +0000 Subject: [PATCH 3/3] Apply review comments --- tests/unit/samples/wiring/service.py | 16 ++-- .../unit/samples/wiringstringids/container.py | 4 +- tests/unit/samples/wiringstringids/module.py | 82 ++++++++++++++----- tests/unit/samples/wiringstringids/service.py | 19 +++++ .../unit/wiring/string_ids/test_main_py36.py | 30 ++++++- 5 files changed, 121 insertions(+), 30 deletions(-) diff --git a/tests/unit/samples/wiring/service.py b/tests/unit/samples/wiring/service.py index 15e2990b..944b9921 100644 --- a/tests/unit/samples/wiring/service.py +++ b/tests/unit/samples/wiring/service.py @@ -5,11 +5,17 @@ class Service: class ServiceWithCallable: def __init__(self): self.foo = CallableDict({"bar": lambda: 10}) - self.method_with_args = lambda x, y: x + y - self.method_with_kwargs = lambda **kwargs: kwargs + + def method_with_args(self, x, y): + return x + y + + def method_with_kwargs(self, **kwargs): + return kwargs class CallableDict(dict): - def __init__(self, data): - super().__init__(data) - self.process = lambda *args, **kwargs: {"args": args, "kwargs": kwargs} + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def process(self, *args, **kwargs): + return {"args": args, "kwargs": kwargs} diff --git a/tests/unit/samples/wiringstringids/container.py b/tests/unit/samples/wiringstringids/container.py index 95681dbf..69eb7115 100644 --- a/tests/unit/samples/wiringstringids/container.py +++ b/tests/unit/samples/wiringstringids/container.py @@ -1,6 +1,6 @@ from dependency_injector import containers, providers -from .service import Service +from .service import Service, ServiceWithCallable class SubContainer(containers.DeclarativeContainer): @@ -14,4 +14,6 @@ class Container(containers.DeclarativeContainer): service = providers.Factory(Service) + service_with_callable = providers.Factory(ServiceWithCallable) + sub = providers.Container(SubContainer) diff --git a/tests/unit/samples/wiringstringids/module.py b/tests/unit/samples/wiringstringids/module.py index aac85aa8..8e6662d2 100644 --- a/tests/unit/samples/wiringstringids/module.py +++ b/tests/unit/samples/wiringstringids/module.py @@ -4,21 +4,20 @@ from typing import Callable from dependency_injector.wiring import ( - inject, Provide, Provider, - as_int, - as_float, as_, - required, + as_float, + as_int, + inject, invariant, provided, + required, ) from .container import Container from .service import Service - service: Service = Provide["service"] service_provider: Callable[..., Service] = Provider["service"] undefined: Callable = Provide["undefined"] @@ -55,22 +54,24 @@ def test_function(service: Service = Provide["service"]): @inject -def test_function_provider(service_provider: Callable[..., Service] = Provider["service"]): +def test_function_provider( + service_provider: Callable[..., Service] = Provider["service"], +): service = service_provider() return service @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)], + 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, @@ -87,25 +88,60 @@ def test_config_value( @inject def test_config_value_required_undefined( - value_required: int = Provide["config.a.b.c", required()], + value_required: int = Provide["config.a.b.c", required()], ): return value_required @inject -def test_provide_provider(service_provider: Callable[..., Service] = Provide["service.provider"]): +def test_provide_provider( + service_provider: Callable[..., Service] = Provide["service.provider"], +): service = service_provider() return service @inject -def test_provider_provider(service_provider: Callable[..., Service] = Provider["service.provider"]): +def test_provider_provider( + service_provider: Callable[..., Service] = Provider["service.provider"], +): service = service_provider() return service @inject -def test_provided_instance(some_value: int = Provide["service", provided().foo["bar"].call()]): +def test_provided_instance( + some_value: int = Provide["service", provided().foo["bar"].call()] +): + return some_value + + +@inject +def test_provided_instance_call_with_args( + some_value: int = Provide[ + "service_with_callable", + provided().method_with_args.call(1, 2), + ], +): + return some_value + + +@inject +def test_provided_instance_call_with_kwargs( + some_value: dict = Provide[ + "service_with_callable", + provided().method_with_kwargs.call(a=1, b=2), + ], +): + return some_value + + +@inject +def test_provided_instance_call_with_args_and_kwargs( + some_value: dict = Provide[ + "service_with_callable", provided().foo.process.call(1, 2, key="value") + ] +): return some_value @@ -115,14 +151,16 @@ def test_subcontainer_provider(some_value: int = Provide["sub.int_object"]): @inject -def test_config_invariant(some_value: int = Provide["config.option", invariant("config.switch")]): +def test_config_invariant( + some_value: int = Provide["config.option", invariant("config.switch")] +): return some_value @inject def test_provide_from_different_containers( - service: Service = Provide["service"], - some_value: int = Provide["int_object"], + service: Service = Provide["service"], + some_value: int = Provide["int_object"], ): return service, some_value diff --git a/tests/unit/samples/wiringstringids/service.py b/tests/unit/samples/wiringstringids/service.py index 4151b94f..944b9921 100644 --- a/tests/unit/samples/wiringstringids/service.py +++ b/tests/unit/samples/wiringstringids/service.py @@ -1,2 +1,21 @@ class Service: service_attr: int + + +class ServiceWithCallable: + def __init__(self): + self.foo = CallableDict({"bar": lambda: 10}) + + def method_with_args(self, x, y): + return x + y + + def method_with_kwargs(self, **kwargs): + return kwargs + + +class CallableDict(dict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def process(self, *args, **kwargs): + return {"args": args, "kwargs": kwargs} diff --git a/tests/unit/wiring/string_ids/test_main_py36.py b/tests/unit/wiring/string_ids/test_main_py36.py index 3a4e344e..4731e876 100644 --- a/tests/unit/wiring/string_ids/test_main_py36.py +++ b/tests/unit/wiring/string_ids/test_main_py36.py @@ -51,18 +51,21 @@ def resourceclosing_container(request): def test_package_lookup(): from samples.wiringstringids.package import test_package_function + service = test_package_function() assert isinstance(service, Service) def test_package_subpackage_lookup(): from samples.wiringstringids.package.subpackage import test_package_function + service = test_package_function() assert isinstance(service, Service) def test_package_submodule_lookup(): from samples.wiringstringids.package.subpackage.submodule import test_function + service = test_function() assert isinstance(service, Service) @@ -75,7 +78,13 @@ def test_module_attributes_wiring(): def test_module_attribute_wiring_with_invalid_marker(container: Container): from samples.wiringstringids import module_invalid_attr_injection - with raises(Exception, match=re.escape("Unknown type of marker {0}".format(module_invalid_attr_injection.service))): + + with raises( + Exception, + match=re.escape( + "Unknown type of marker {0}".format(module_invalid_attr_injection.service) + ), + ): container.wire(modules=[module_invalid_attr_injection]) @@ -182,7 +191,7 @@ def test_configuration_option(): def test_configuration_option_required_undefined(container: Container): container.config.reset_override() - with raises(errors.Error, match="Undefined configuration option \"config.a.b.c\""): + with raises(errors.Error, match='Undefined configuration option "config.a.b.c"'): module.test_config_value_required_undefined() @@ -205,6 +214,21 @@ class TestService: assert some_value == 10 +def test_provided_instance_call_with_args(): + some_value = module.test_provided_instance_call_with_args() + assert some_value == 3 + + +def test_provided_instance_call_with_kwargs(): + some_value = module.test_provided_instance_call_with_kwargs() + assert some_value == {"a": 1, "b": 2} + + +def test_provided_instance_call_with_args_and_kwargs(): + some_value = module.test_provided_instance_call_with_args_and_kwargs() + assert some_value == {"args": (1, 2), "kwargs": {"key": "value"}} + + def test_subcontainer(): some_value = module.test_subcontainer_provider() assert some_value == 1 @@ -260,11 +284,13 @@ def test_unwire_class_method(container: Container): def test_unwire_package_function(container: Container): container.unwire() from samples.wiringstringids.package.subpackage.submodule import test_function + assert isinstance(test_function(), Provide) def test_unwire_package_function_by_reference(container: Container): from samples.wiringstringids.package.subpackage import submodule + container.unwire() assert isinstance(submodule.test_function(), Provide)