python-dependency-injector
python-dependency-injector copied to clipboard
Inconsistent behavior of `providers.Self()` used with `@containers.copy(...)`, which gives not a container instance but a type
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__(...)expectscontaineras an instance ofcontainers.Containertype. - that container is provided via
__self__which isproviders.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
Sounds like a duplicate of #619