question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Resource is not detected by container.init_resources() when declaration is not at class root level

See original GitHub issue

Howdy, 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:open
  • Created 3 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
rmk135commented, Jan 29, 2021

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:

object = SomeClass(
    resource1=await resource1(),
    resource2=await resource2(),
    resource3=await resource3(),
)

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:

resource1, resource2, resource3 = await asyncio.gather(resource1(), resource2(), resource3())

object = SomeClass(
    resource1=resource1,
    resource2=resource2,
    resource3=resource3,
)

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.

  1. You need to await container.some_service. It’s needed because container.some_service has async dependency that turns it into async mode.
async def main():
	container = SomeContainer()
	await container.init_resources()
	service = await container.some_service()  # <-- Add await here
	service.hello_world()  # Actual service here!
	await container.shutdown_resources()
  1. Keep resources list flat to have await container.init_resources() and await container.shutdown_resources() working properly. This is a temporary measure. I’ll fix it.
0reactions
rmk135commented, Feb 2, 2021

Example with callable is terrific:

async def main():
	container = SomeContainer()
	await container.init_resources()
	container.main()
	await container.shutdown_resources()

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 first async / 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:

    for resource in container.traverse():
        resource.enable_async_mode()

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Classpath resource not found when running as jar
resource.getFile() expects the resource itself to be available on the file system, i.e. it can't be nested inside a jar file.
Read more >
Diff - platform/frameworks/layoutlib - android Git repositories
getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, "Integer resource array contains non-integer value: \"" + - resValue.getElement(i) + "\"", null); + resValue.
Read more >
Jakarta RESTful Web Services - Quarkus
Non -root resource classes are instantiated by an application and do not require the above-described public constructor.
Read more >
The ResourceConfig instance does not contain an... - JBoss.org
container.ContainerException: The ResourceConfig instance does not contain any root resource classes. at com.sun.jersey.server.impl.application.
Read more >
Chapter 3. JAX-RS Application, Resources and Sub-Resources
Root resource classes are POJOs (Plain Old Java Objects) that are annotated ... If a user name does not match that a 404...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found