ets-labs / python-dependency-injector

Dependency injection framework for Python
https://python-dependency-injector.ets-labs.org/
BSD 3-Clause "New" or "Revised" License
3.72k stars 300 forks source link

Dependencies are not injected when creating injections into a module using "providers.DependenciesContainer" #551

Open satodaiki opened 2 years ago

satodaiki commented 2 years ago

hello.

The following code tries to use SubContainer to perform dependency injection into a Service, but this code cannot be executed with the exception "exception: no description".

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide

class Service:
    ...

class Container(containers.DeclarativeContainer):

    sub_container = providers.DependenciesContainer()

class SubContainer(containers.DeclarativeContainer):

    service = providers.Factory(Service)

service: Service = Provide[Container.sub_container.service]

if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])

    assert isinstance(service, Service)

How can I perform dependency injection into a SubContainer?

rowan-maclachlan commented 2 years ago

I'm having a similar issue. I'm coming from Spring DI and Google Guice land, and some aspects of this DI framework leave me scratching my head. The only way I see to get something like this working would be pass the SubContainer instance to the Container and leverage it that way. i.e.

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide

class Service:
    ...

class Container(containers.DeclarativeContainer):

    sub_container = providers.DependenciesContainer()
    service=sub_container.service

class SubContainer(containers.DeclarativeContainer):

    service = providers.Factory(Service)

service: Service = Provide[Container.service]

if __name__ == "__main__":
    container = Container(sub_container=SubContainer())
    container.wire(modules=[__name__])

    assert isinstance(service, Service)

Or something like that. I'm not sure my example is right or that I understood your problem, exactly. My issue is handling the following case without too much boilerplate - when you have a chain of dependencies, with each dependency at a different architectural level.

adapter_dep <- usecase_dep <- application

And each dependency is provided in a different container - i.e. each architectural level has a container that provides the dependency. Really, one doesn't need a container to provide the application dependency, because you can wire it with the usecase container:

class Application():
  def __init__(self, usecase_dep: Usecase=Provide("usecase_dep")):
    ...

And the usecase:

class Usecase():
  def __init__(self, dep1: DependencyA=Provide("dep1"), dep2: DependencyB=Provide("dep2")):
  ...

and then there's the container...

class UsecaseContainer(containers.DeclarativeContainer):

  adapter_container: AdapterContainer = providers.DependenciesContainer() 
  usecase_dep = providers.Singleton(
        Usecase,
        dep1=adapter_container.dep1, <- I want to wire these automatically
        dep2=adapter_container.dep2
    )

I want to provide the usecase dependencies for the Application classes, but I don't want to have to spell out how to create each usecase in the container - I might have dozens, and I feel like it could be handled automatically if I could get the containers and the wiring correct, because I'm using Provide in the usecase constructor, and my AdapterContainer can wire the module with its dependencies... But then how do I wire the Application module?

Anyways, I don't want to steal your thunder, I just thought these difficulties might be related. Please report back if you make progress.