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

Ability to use provider method/property in Singleton or Callable Provider

Open rustam-ashurov-mcx opened this issue 3 years ago • 5 comments

Hi, probably a simple question, but I can not find a straight answer anywhere on it:

  • Is there a way to use a method of one provider in another if another provider is a Singletone or Callable provider?

So I want to do something like this (and in an ideal way to have proper typings for mypy check):

    factory= providers.Factory(Factory)
    service= providers.Callable(factory.create_service)

I already know that it will work with:

    factory= providers.Factory(Factory)
    service= providers.Callable(factory().create_service)

However, I would like to make it clean and postpone factory instantiation up to the moment when service will be required

I have seen this page about "Injecting provided object attributes" : https://python-dependency-injector.ets-labs.org/providers/provided_instance.html And it looks like a perfect solution for me, but this code doesn't work:

    factory= providers.Factory(Factory)
    service= providers.Callable(factory.provided.create_service)

Maybe because 'provided' works only when it is used as a dependency for another Provider

So, yeah, would be happy for any suggestions

rustam-ashurov-mcx avatar May 19 '22 18:05 rustam-ashurov-mcx

Do you have a minimal working example that shows your issue?

chbndrhnns avatar May 20 '22 06:05 chbndrhnns

For sure:

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

class Service:
    def hello(self):
        print("Hello World")

class ServiceFactory:
    def create_service(self):
        return Service()

class Container(containers.DeclarativeContainer):
    service_factory = providers.Factory(ServiceFactory)
    # service = providers.Callable(service_factory.provides.create_service) # doesn't work
    # service = providers.Callable(service_factory.provider.create_service) # doesn't work
    # service = providers.Callable(service_factory.provided.create_service) # doesn't work
    service = providers.Callable(service_factory().create_service) # works, but requires to instantiate factory during this registration


@inject
def main(service: Service = Provide[Container.service]) -> None:
    service.hello()


container = Container()
container.wire(modules=[__name__])
main()

rustam-ashurov-mcx avatar May 23 '22 07:05 rustam-ashurov-mcx

Using provided instead of provides and calling service where its injected works for me:

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


class Service:
    def hello(self):
        print("Hello World")


class ServiceFactory:
    def create_service(self):
        return Service()


class Container(containers.DeclarativeContainer):
    service_factory = providers.Factory(ServiceFactory)
    service = providers.Callable(service_factory.provided.create_service)


@inject
def main(service: providers.Container[Service] = Provide[Container.service]) -> None:
    service().hello()


container = Container()
container.wire(modules=[__name__])
main()

chbndrhnns avatar May 23 '22 08:05 chbndrhnns

Interesting but is there any way to inject service as it is and not call: service().hello() but service.hello()

rustam-ashurov-mcx avatar May 23 '22 08:05 rustam-ashurov-mcx

is there any way to inject service as it is and not call

Not from my experience but I might be mistaken.

chbndrhnns avatar May 23 '22 08:05 chbndrhnns

@rustam-ashurov-mcx Probably late but shouldn't you have used .call()?

Does that work the way you wanted?

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


class Service:
    def hello(self):
        print("Hello World")


class ServiceFactory:
    def create_service(self):
        return Service()


class Container(containers.DeclarativeContainer):
    service_factory = providers.Factory(ServiceFactory)

    service = providers.Callable(
        service_factory.provided.create_service.call()
    )


@inject
def main(service: Service = Provide[Container.service]) -> None:
    service.hello()


container = Container()
container.wire(modules=[__name__])
main()

xunto avatar Sep 01 '22 16:09 xunto

@rustam-ashurov-mcx Probably late but shouldn't you have used .call()?

Does that work the way you wanted?

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


class Service:
    def hello(self):
        print("Hello World")


class ServiceFactory:
    def create_service(self):
        return Service()


class Container(containers.DeclarativeContainer):
    service_factory = providers.Factory(ServiceFactory)

    service = providers.Callable(
        service_factory.provided.create_service.call()
    )


@inject
def main(service: Service = Provide[Container.service]) -> None:
    service.hello()


container = Container()
container.wire(modules=[__name__])
main()

really appreciate this response, having it I was able to find a page with documentation, and yes, it's exactly what I was looking for, many thanks : )

rustam-ashurov-mcx avatar Sep 01 '22 17:09 rustam-ashurov-mcx