Skip to content

Examples

Roberto Prevato edited this page Jan 31, 2021 · 11 revisions

Type resolution by class annotations

new in version 1.1.0 ⭐

Note how class annotations are used in the example below to define dependencies between services. rodi can resolve dependencies by class annotations: db_settings: DatabaseSettings in the PostgresCatsRepository type, and cats_repository: ICatsRepository in GetCatRequestHandler:

from abc import ABC, abstractmethod
from dataclasses import dataclass

from rodi import Container


# domain object:
class Cat:
    def __init__(self, name):
        self.name = name


# abstract interface
class ICatsRepository(ABC):
    @abstractmethod
    def get_by_id(self, _id) -> Cat:
        pass


@dataclass
class DatabaseSettings:
    connection_string: str


class PostgresCatsRepository(ICatsRepository):
    db_settings: DatabaseSettings

    def get_by_id(self, _id) -> Cat:
        # TODO: implement logic to use a connection to the db
        return Cat("...")


class GetCatRequestHandler:
    cats_repository: ICatsRepository

    def get_cat(self, _id):
        cat = self.cats_repository.get_by_id(_id)
        return cat


container = Container()
container.add_instance(DatabaseSettings(connection_string="<YOUR_CONNECTION_STRING>"))
container.add_transient(ICatsRepository, PostgresCatsRepository)
container.add_exact_transient(GetCatRequestHandler)
provider = container.build_provider()

cats_repo = provider.get(ICatsRepository)
assert isinstance(cats_repo, PostgresCatsRepository)
assert isinstance(cats_repo.db_settings, DatabaseSettings)

get_cat_handler = provider.get(GetCatRequestHandler)
assert isinstance(get_cat_handler, GetCatRequestHandler)
assert isinstance(get_cat_handler.cats_repository, PostgresCatsRepository)

Type resolution by type hints inside constructors

Consider the example below:

from abc import ABC, abstractmethod

# domain object:
class Cat:

    def __init__(self, name):
        self.name = name

# abstract interface
class ICatsRepository(ABC):

    @abstractmethod
    def get_by_id(self, _id) -> Cat:
        pass


# one of the possible implementations of ICatsRepository
class InMemoryCatsRepository(ICatsRepository):

    def __init__(self):
        self._cats = {}

    def get_by_id(self, _id) -> Cat:
        return self._cats.get(_id)


# NB: example of business layer class, using interface of repository
class GetCatRequestHandler:

    def __init__(self, cats_repository: ICatsRepository):
        self.repo = cats_repository

rodi configuration to obtain instances of GetCatRequestHandler would look like this:

# (imports omitted...)
from rodi import Container

container = Container()

# configuring container...
container.add_transient(ICatsRepository, InMemoryCatsRepository)
container.add_exact_transient(GetCatRequestHandler)

# building the provider
services = container.build_provider()

# obtaining instances of container:
get_cat_handler = services.get(GetCatRequestHandler)

assert isinstance(get_cat_handler, GetCatRequestHandler)
assert isinstance(get_cat_handler.repo, InMemoryCatsRepository)

Type resolution by parameter names

In this example, type resolution happens by parameter name, when requiring Foo service, a new one is instantiated passing the configured singleton of Settings to its constructor.

class Foo:

    def __init__(self, settings):
        self.settings = settings


class Settings:
    def __init__(self, db_connection_string):
        self.db_connection_string = db_connection_string


# in some other module..
from rodi import Container

container = Container()

# configuring container...
container.add_instance(Settings("whatever/connection/string"))
container.add_exact_transient(Foo)

# building the provider
services = container.build_provider()

# obtaining instances of container:
foo = services.get(Foo)

assert isinstance(foo, Foo)
assert isinstance(foo.settings, Settings)

More examples in tests code

Please look at tests code, for more examples.