Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm using the injector module that embodies most of the Guice API for a DI framework.

What I am trying to accomplish what Guice does with the @Named binding annotation. That is to say, using the injector.Module to override the configure method and use the Binder to bind multiple instances of the same type, but with different names:

import pathlib
import injector

ConfigFile = pathlib.WindowsPath
UserFile = pathlib.WindowsPath

cfg_file = pathlib.Path.home() / 'settings.cfg'
usr_file = pathlib.Path.home() / 'user.inf'

class AppModule(injector.Module):

    def configure(self, binder):
        binder.bind(ConfigFile, to=cfg_file) # Guice uses annotatedWith here
        binder.bind(UserFile, to=usr_file) # Guice uses annotatedWith here

class App:

    @injector.inject
    def __init__(self, cfg: ConfigFile, usr: UserFile):
        self.config = cfg
        self.user = usr

app = injector.Injector(AppModule).get(App)
print(app.config, app.user) # prints the contents of `usr_file` for both

I thought that Binder.multibind my have some viability, but I've been unsuccessful in my attempts. They only solution I can devise is one in which pathlib.Path must be subclassed and those subclasses explicitly bound to their instances (which can be manged in the injector.Module using @provider in conjunction with @singleton).

Is there another way to accomplish this that does not resolve to sublcasses?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
316 views
Welcome To Ask or Share your Answers For Others

1 Answer

It's possible to use Annotated from either typing_extensions (already an injector's dependency or from Python 3.9 typing). In your example above just replace

ConfigFile = pathlib.WindowsPath
UserFile = pathlib.WindowsPath

with

ConfigFile = Annotated[pathlib.WindowsPath, 'config']
UserFile = Annotated[pathlib.WindowsPath, 'user']

And it should work.

It's all possible to use typing.NewType but it probably won't play nicely with static type checkers.

Here's a full working example tested on python 3.6:

from injector import Injector, inject, Module, singleton, provider
from typing_extensions import Annotated

from typing import NewType, Callable

# You can use Greeting instead of Annotated[str, 'greeting'] as it's the same type
Greeting = Annotated[str, 'greeting']


class Greeter:
    @inject
    def __init__(self, greeting: Annotated[str, 'greeting']):
        self.greeting = greeting

    def greet(self):
        print(self.greeting)


class MyModule(Module):

    def configure(self, binder):
        binder.bind(Greeting, to='Howdy', scope=singleton)
        binder.bind(str, to='Hello', scope=singleton)


def main():
    injector = Injector([MyModule()])
    greeter = injector.get(Greeter)
    greeter.greet()


if __name__ == '__main__':
    main()

There's a bit of a problem, if you don't provide a binding for Annotated[str, 'greeting'] injector will use a default constructor which produces something, but probably not what you would expect. On the other hand it handles str just the same - happily constructs an empty string.

Tests in the conda environment with the following packages installed (injector is installed using pip):

# Name                    Version
injector                  0.18.4 
libffi                    3.0.9  
pip                       18.1   
python                    3.6.8  
setuptools                40.8.0 
sqlite                    3.26.0 
typing-extensions         3.7.4.3
wheel                     0.33.1 
xz                        5.2.3  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...