python-dependency-injector icon indicating copy to clipboard operation
python-dependency-injector copied to clipboard

Inconsistent behavior of `providers.Self()` used with `@containers.copy(...)`, which gives not a container instance but a type

Open elbakramer opened this issue 2 years ago • 1 comments
trafficstars

From the following documentation and sample code:

https://python-dependency-injector.ets-labs.org/providers/inject_self.html

from dependency_injector import containers, providers


class Service:
    def __init__(self, name: str):
        self.name = name


class ServiceDispatcher:
    def __init__(self, container: containers.Container):
        self.container = container

    def get_services(self):
        for provider in self.container.traverse(types=[providers.Factory]):
            yield provider()


class Container(containers.DeclarativeContainer):

    __self__ = providers.Self()

    service1 = providers.Factory(Service, name="Service 1")
    service2 = providers.Factory(Service, name="Service 2")
    service3 = providers.Factory(Service, name="Service 3")

    dispatcher = providers.Singleton(ServiceDispatcher, __self__)


if __name__ == "__main__":
    container = Container()

    dispatcher = container.dispatcher()
    for service in dispatcher.get_services():
        print(service.name)

I can see that:

  • the ServiceDispatcher's __init__(...) expects container as an instance of containers.Container type.
  • that container is provided via __self__ which is providers.Self().

I can reproduce this with a similar code (case 1 in main.py). But when I do a similar thing with a usage of @container.copy(...), weird thing happens (case 2 in main.py):

The provided container argument is no longer an instance but a type of containers.Container.

Am I missing something?

Below is a sample code that can reproduce this behavior:

main.py:

from dependency_injector import containers, providers


class SingletonClass:
    def __init__(self, container):
        print(container)
        print(type(container))
        if isinstance(container, type):
            print("given container is not an instance but a type")
            container = container()
            print(container)
            print(type(container))
        self.container = container


class ContainerWithResourceAndSingleton(containers.DeclarativeContainer):

    container = providers.Self()
    resource = providers.Resource(providers.Object(None))
    singleton = providers.Singleton(SingletonClass, container=container)


print("case 1")
container = ContainerWithResourceAndSingleton()
singleton = container.singleton()
singleton.container.init_resources()
singleton.container.shutdown_resources()


class ContainerWithResource(containers.DeclarativeContainer):

    resource = providers.Resource(providers.Object(None))


@containers.copy(ContainerWithResource)
class ContainerWithCopiedResourceAndSingleton(ContainerWithResource):

    container = providers.Self()
    singleton = providers.Singleton(SingletonClass, container=container)


print("case 2")
container = ContainerWithCopiedResourceAndSingleton()
singleton = container.singleton()
singleton.container.init_resources()
singleton.container.shutdown_resources()

console:

$ python main.py
case 1
<dependency_injector.containers.DynamicContainer object at 0x000001E5FB515F70>
<class 'dependency_injector.containers.DynamicContainer'>
case 2
<class '__main__.ContainerWithCopiedResourceAndSingleton'>
<class 'dependency_injector.containers.DeclarativeContainerMetaClass'>
given container is not an instance but a type
<dependency_injector.containers.DynamicContainer object at 0x000001E5FC936F10>
<class 'dependency_injector.containers.DynamicContainer'>

Other environmental settings are the followings:

  • OS: Windows 11
  • Python: Anaconda Python 3.9.7
  • Dependency Injector: 4.41.0

elbakramer avatar Mar 03 '23 01:03 elbakramer

Sounds like a duplicate of #619

Qjammer avatar Mar 27 '23 09:03 Qjammer