python-injector / injector

Python dependency injection framework, inspired by Guice
BSD 3-Clause "New" or "Revised" License
1.31k stars 81 forks source link

Support eager singleton #44

Open richburdon opened 9 years ago

richburdon commented 9 years ago

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):
alecthomas commented 9 years ago

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

richburdon commented 9 years ago

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.

alecthomas commented 9 years ago

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 ;)

jstasiak commented 9 years ago

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

richburdon commented 9 years ago

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 notifications@github.com 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.

juharris commented 8 years ago

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.

jstasiak commented 7 years ago

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.

comtihon commented 5 years ago

@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.

jstasiak commented 5 years ago

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

juharris commented 5 years ago

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.

Sytten commented 5 years ago

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.

levsa commented 4 years ago

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.

delucca commented 1 year ago

+1 for this feature

delucca commented 1 year ago

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.