injector icon indicating copy to clipboard operation
injector copied to clipboard

Support eager singleton

Open richburdon opened this issue 8 years ago • 14 comments

There doesn't seem to be a way to bind eager singletons. The only reference is in the Module's provides method, but this is unreferenced.

def provides(interface, scope=None, eager=False):

richburdon avatar Jul 29 '15 20:07 richburdon

Yes, I should remove that from the method signature. I don't think it will be added at this stage.

alecthomas avatar Aug 17 '15 00:08 alecthomas

Not added because it's not under active development or a time issue? Or not useful/practical?

BTW, are you still actively developing the project? Is there a better alternative?

Thanks.

richburdon avatar Aug 18 '15 17:08 richburdon

Mostly the latter; not useful enough to warrant the effort IMO.

Yes, Injector is still being developed, it's just mostly stable so there's not much activity.

You can have a look at pinject, but it hasn't been touched in two years and IMO is not as nice as Injector anyway ;)

alecthomas avatar Aug 18 '15 21:08 alecthomas

I can confirm that the lack of activity is due to the code just working.

jstasiak avatar Sep 17 '15 22:09 jstasiak

Fair enough :).

I find myself in a minority of Python developers who can't live without DI, so thanks for this package.

I do however, think eager singletons are important. As would like to see Sequence and Map keys support instantiation by class (rather than by instance). I'll try to send a pull request for the workarounds that I have created.

On Thu, Sep 17, 2015 at 6:14 PM Jakub Stasiak [email protected] wrote:

I can confirm that the lack of activity is due to the code just working.

— Reply to this email directly or view it on GitHub https://github.com/alecthomas/injector/issues/44#issuecomment-141251598.

richburdon avatar Oct 16 '15 04:10 richburdon

On a similar note, I'd like to be able instantiate all singletons when my server starts, something analogous to Guice's Stage.PRODUCTION. Any particular reasons why it's not supported? Would you mind if I added it? I'd probably do it as a stage parameter to Injector.

juharris avatar Aug 22 '16 18:08 juharris

I completely forgot this issue was still open :) I wouldn't be against it in principle, I'm just wondering what the actual implementation would look like.

jstasiak avatar Oct 19 '17 17:10 jstasiak

@jstasiak It is used to catch configuration errors on startup, when spawning all singleton services. F.e. I've introduced a breaking change (configuration/compilation error/broke all injections by binding unknown/wrong module). Then I start the service and it is ok till any of injected services will be used.

Another issue is - run some things, like loading and configuring some RO model (~400MB) before spawning workers, so they won't make memory copies of this model.

comtihon avatar Nov 07 '18 15:11 comtihon

Yeah I'm not saying it's not useful, it's the implementation I wondered about, the general case seems tricky. For example:


@singleton
SomeClass:
    pass


injector = Injector()
eagerly_instantiate_singletons(injector)

With auto_bind enabled (the default) Injector doesn't know anything about SomeClass until it's instantiated so the eager instantiation mechanism would need to actively look for classes (All loaded classes? Classes in a particular module?).

Depending on the application the easiest and the most helpful could be to do

for t in [list, of, some, known, singletons, that, can, cause, trouble]:
    injector.get(t)

at the start of your program.

BTW not sure if memory saving would work in the scenario you describe, see https://instagram-engineering.com/dismissing-python-garbage-collection-at-instagram-4dca40b29172

jstasiak avatar Nov 07 '18 15:11 jstasiak

So for Guice it only knows about the singletons mentioned in the module or through dependencies. It would be a great start to instantiate those.

juharris avatar Nov 07 '18 15:11 juharris

I found myself wanting this feature quite badly. I use Injector to inject my service layer in my flask (flask-rebar to be precise) controllers. Some services need to fetch external dependencies, so the first call to the API takes a big hit in performances. I will try the trick of forcing the injector to get the type, but an eager parameter in the @singleton decorator would be much cleaner.

Sytten avatar Oct 16 '19 03:10 Sytten

I would like to request a feature like this as well. I would like to be able to annotate singletons so that they are automatically instantiated upon creation of the injector.

The reason is that I have a module that is to be included by many injectors that have different set of modules, and it would be nice to not need to do injector.get(MySingleton) from all the places where injectors are created.

levsa avatar Feb 28 '20 13:02 levsa

+1 for this feature

delucca avatar Mar 21 '23 17:03 delucca

in the meantime, I would like to share my solution:

from typing import Callable

from injector import Binder


def eager_initialize(eager_modules: list):
    def decorate_function(configure: Callable):
        def wrapper(self, binder: Binder, *args, **kwargs):
            result = configure(self, binder, *args, **kwargs)

            for module in eager_modules:
                binder.injector.get(module)

            return result

        return wrapper

    return decorate_function

You can use it as a decorator in your configure Module method, like this:

class RequestedManufacturerNameModule(Module):
    @eager_initialize([RequestedManufacturerNameService])
    def configure(self, binder: Binder) -> None:
        binder.bind(RequestedManufacturerNameService, scope=singleton)
        # ...

You can pass a list of eager classes to it and it should work. But, in any case, it would be way better doing so in the binder directly.

delucca avatar Mar 21 '23 18:03 delucca