Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More documentation or examples? #104

Open
javierseixas opened this issue May 27, 2024 · 7 comments
Open

More documentation or examples? #104

javierseixas opened this issue May 27, 2024 · 7 comments

Comments

@javierseixas
Copy link

Hello! I'm interested in using this library, but it's being difficult to understand how it works, just reading the README file. Is there more information or samples so I can understand better how configure the container.

Thanks.

@ivankorobkov
Copy link
Owner

Hello.

First, start by configuring an injector. Usually, it is done at the start of your application.

inject.configure(my_config)

Specify optional providers in the config, or let inject automatically construct singletons for you:

def my_config(binder):
  binder.bind(Cache, RedisCache('localhost:1234'))
  binder.bind_to_provider(DB, SQLDatabase)

Use inject to inject instances:

class User:
  db = inject.attr(DB)

  @classmethod
  def get_by_id(cls, id):
    row = cls.db.fetch('users', id)
    return ...

Use other ways to inject dependencies, i.e. inject.autoparams, etc.

--

See something like https://stackoverflow.com/questions/130794/what-is-dependency-injection to better understand concepts.

@javierseixas
Copy link
Author

javierseixas commented May 29, 2024

Thanks for your response, it's the same that appears in the README, though 😅

Can I build a decorator pattern using inject? This pattern uses the same interface in several classes, so for instance, I have this interface:

from abc import ABC, abstractmethod


class Interface(ABC):
    @abstractmethod
    def say_hello(self):
        pass

This class implementing the previous interface:

import inject


class Decorator(Interface):
    decorated: Decorated = inject.attr(Decorated)

    def say_hello(self):
        print("Hello")
        self.decorated.say_hello()

And this other implementation of the interface:

class Decorated(Interface):
    def say_hello(self):
        print("Hello World!")

Having that, when I set my config like this:

def my_config(binder):
    binder.bind(Interface, Decorator())
    binder.bind(Interface, Decorated())

I get this error: inject.InjectorException: Duplicate binding, key=<class 'interface.Interface'>

What I expect from your examples is that I can define several singletons based on the same Interface: Cache is the interface and RedisCache its implementation.

Anyhow, how I could implement decorator or strategy patterns, where I can have more than one singleton implementing the same interface?

Thanks for your help.

@ivankorobkov
Copy link
Owner

Anyhow, how I could implement decorator or strategy patterns, where I can have more than one singleton implementing the same interface?

Well, you can't.

The injector gives you the exact error here:

    binder.bind(Interface, Decorator())
    binder.bind(Interface, Decorated())

You can't bind multiple values to the same type.

What instance, Decorator or Decorated, should be injected here?

@inject.param(Interface)
def function(iface):
  pass

@javierseixas
Copy link
Author

I see your point. So, answering my initial question: the way of doing this that comes to my mind would be something injecting the configuration, like this:

import inject

from abc import ABC, abstractmethod


class Interface(ABC):
    @abstractmethod
    def say_hello(self):
        pass


class NerdDecorator(Interface):
    def say_hello(self):
        print("Hello World!")


class FrenchDecorator(Interface):
    decorated: Interface = inject.attr("nerd")

    def say_hello(self):
        print("Salut!")
        self.decorated.say_hello()


class EnglishDecorator(Interface):
    decorated: Interface = inject.attr("french")

    def say_hello(self):
        print("Hello")
        self.decorated.say_hello()


class CatalanDecorator(Interface):
    decorated: Interface = inject.attr("english")

    def say_hello(self):
        print("Hola")
        self.decorated.say_hello()


def my_config(binder):
    binder.bind("catalan", CatalanDecorator())
    binder.bind("english", EnglishDecorator())
    binder.bind("french", FrenchDecorator())
    binder.bind("nerd", NerdDecorator())
    pass


inject.configure(my_config)


decorator = CatalanDecorator()
decorator.say_hello()

Resulting on:

Hola
Hello
Salut!
Hello World!

So I'm manually creating the singletons and indicating where to inject them. Is there a better way you may suggest, @ivankorobkov ?

@ivankorobkov
Copy link
Owner

Yes, you are right.

I would also mention that it's better to use types, not strings. I think it is easier to reason about the application structure as a typed object graph.

Also using types usually allows to omit binding specifications.

class NerdDecorator(Interface):
    def say_hello(self):
        print("Hello World!")

class FrenchDecorator(Interface):
    decorated: Interface = inject.attr(NerdDecorator)
    def say_hello(self):
        print("Salut!")
        self.decorated.say_hello()

class EnglishDecorator(Interface):
    decorated: Interface = inject.attr(FrenchDecorator)
    def say_hello(self):
        print("Hello")
        self.decorated.say_hello()

class CatalanDecorator(Interface):
    decorated: Interface = inject.attr(EnglishDecorator)
    def say_hello(self):
        print("Hola")
        self.decorated.say_hello()


def my_config(binder):
    pass

inject.configure(my_config)

decorator = CatalanDecorator()
decorator.say_hello()

also prints:

Hola
Hello
Salut!
Hello World!

@javierseixas
Copy link
Author

Thanks for all your explanations. What do you think about including some more samples in the README? Can I propose a PR with that?

@ivankorobkov
Copy link
Owner

Sure, I you make a PR, I will merge it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants