Skip to content
Closed
10 changes: 5 additions & 5 deletions src/dependency_injector/wiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,14 +598,14 @@ 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

if not arg.args and isinstance(arg, providers.Resource):
if isinstance(arg, providers.Resource):
return {str(id(arg)): arg}
Copy link

@mspiridonov2706 mspiridonov2706 Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @jazzthief. Fix not working for several Resources. Maybe something like this?

Suggested change
return {str(id(arg)): arg}
closing_deps |= {str(id(arg)): arg}

else:
closing_deps += _locate_dependent_closing_args(arg)
if arg.args or arg.kwargs:
closing_deps |= _locate_dependent_closing_args(arg)

return closing_deps


Expand Down
53 changes: 52 additions & 1 deletion tests/unit/samples/wiringstringids/resourceclosing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@
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):
cls.init_counter = 0
cls.shutdown_counter = 0

@classmethod
def init(cls):
def init(cls, dependency: Singleton = None):
if dependency:
cls.dependency = dependency
cls.init_counter += 1

@classmethod
Expand All @@ -25,17 +32,49 @@ 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()
yield 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
)
nested_service = providers.Factory(NestedService, factory_service)


class ContainerSingleton(containers.DeclarativeContainer):
Copy link
Contributor Author

@jazzthief jazzthief May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added another container to cover a case where a resource is not at the end of the dependency graph. A Singleton provider type is arbitrary - its type doesn't play any role in injections, just needed something for a Resource to depend on.


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)


@inject
Expand All @@ -46,3 +85,15 @@ 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


@inject
def test_function_nested_dependency(
nested: NestedService = Closing[Provide["nested_service"]]
):
return nested
36 changes: 33 additions & 3 deletions tests/unit/wiring/string_ids/test_main_py36.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -303,6 +303,36 @@ def test_closing_dependency_resource():
assert result_2.service.init_counter == 2
assert result_2.service.shutdown_counter == 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")
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


Expand Down