Resource is not detected by container.init_resources() when declaration is not at class root level
See original GitHub issueHowdy, another somewhat similar issue like #379, this time with providers.Resource
.
Consider this example:
from dependency_injector import providers, containers
import asyncio
class TestObject():
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
def sync_foo(foo):
print('sync foo triggered!')
return foo
async def async_foo(foo):
print('async foo triggered!')
await asyncio.sleep(0)
return foo
class RootResourceContainer(containers.DeclarativeContainer):
sync_foo_resource=providers.Resource(
sync_foo,
foo='bar'
)
async_foo_resource=providers.Resource(
async_foo,
foo='bar'
)
obj_factory = providers.DelegatedFactory(
TestObject,
foo=async_foo_resource,
bar=sync_foo_resource,
)
class NonRootResourceContainer(containers.DeclarativeContainer):
obj_factory = providers.DelegatedFactory(
TestObject,
foo=providers.Resource(
async_foo,
foo='bar'
),
bar=providers.Resource(
sync_foo,
foo='bar'
)
)
async def main():
container = RootResourceContainer()
await container.init_resources()
print('after init')
print(container.obj_factory())
await container.shutdown_resources()
print('----')
container = NonRootResourceContainer()
await container.init_resources() # Crashing as async non-root async Resource is not detected
print('after init')
print(container.obj_factory())
await container.shutdown_resources()
asyncio.run(main())
First container with everything defined at class root level works, as both Resources are detected and initialized by container.init_resources()
.
Second container with nested Resources definition can’t figure it out, and container.init_resources()
is not picking up all resources as stated in docs. In result, if no async resources are found, container.init_resources()
will not be awaitable, and awaiting it will crash.
Also in first container by calling container.obj_factory()
we are receiving awaitable, not actual factory result. As I’m expecting to receive awaitable when there was no previous async Resource initialization and handle this somewhere in my code (as stated in docs), that I’m expecting container.init_resources()
to do the job and resolve every declared resource and get rid of awaiting anything inside my code. Or in another words - I’d like to keep Resource-specific logic at the container layer, without bleeding it into my code, which will be sensitive for IoC layer changes.
Issue Analytics
- State:
- Created 3 years ago
- Comments:7 (5 by maintainers)
Top GitHub Comments
I had the same vision on async resources when started working on the feature. I thought about
Resource
provider as a connector between async and sync worlds. While I proceed with implementation and testing, I realized that it leads to undesired side effects. To avoid the side effects I decided to make it work truly async on all levels.The goods news is that truly async design brings outstanding feature: framework can collect async dependencies asynchronously. For instance next code will block 3 times:
Resource 2 will start initialization only after resource 1 is ready, resource 3 - only after 2 is ready.
Dependency Injector prepares all dependencies concurrently instead:
Resource 1,2,3 are initialized at the same time. Injections are done when the last is ready.
As of your example, there are 2 things.
container.some_service
. It’s needed becausecontainer.some_service
has async dependency that turns it into async mode.await container.init_resources()
andawait container.shutdown_resources()
working properly. This is a temporary measure. I’ll fix it.Example with callable is terrific:
I have no idea why that works 😃 I mean why
container.main()
works without await. I have feeling I need to fix something to make it stop working 😃As of the
.synchronized
. It’s just an idea for now. I will experiment if it’s possible to make it work properly.Despite
.synchronized
experiment, I would recommend to check if you could go async on all the layers of your application. Python works that way that with a firstasync / await
you turn your application into asynchronous mode. Dependency Injector async mode feature is designed the way to handle this properly: not synchronize, but pass through. If your application already handles asynchronous call on the top level (main function, endpoints etc) then you could turn all providers in async mode explicitly:In that case you will need to always use await to get dependencies from the container.
If you have any other thoughts or idea - please, share. Feedback helps to improve the framework.