Skip to content

Registration and Binding

Provider registration serves two important functions:

  • A way to tell the container how to assemble things that can't be auto-wired, for example interfaces.
  • A way to override dependencies in tests.

We call a the result of registering a provider a bind.

Every bind in di consists of:

  • A target callable: this can be a function, an interface / protocol or a concrete class
  • A substitute dependency: an object implementing the DependantBase, usually just an instance of Dependant

This means that binds are themselves dependencies:

import sys
from dataclasses import dataclass

if sys.version_info < (3, 8):
    from typing_extensions import Protocol
else:
    from typing import Protocol

from di import AsyncExecutor, Container, Dependant


class DBProtocol(Protocol):
    async def execute(self, sql: str) -> None:
        ...


async def controller(db: DBProtocol) -> None:
    await db.execute("SELECT *")


@dataclass
class DBConfig:
    host: str = "localhost"


class Postgres(DBProtocol):
    def __init__(self, config: DBConfig) -> None:
        self.host = config.host

    async def execute(self, sql: str) -> None:
        print(sql)


async def framework() -> None:
    container = Container(scopes=("request",))
    container.register_by_type(Dependant(Postgres, scope="request"), DBProtocol)
    solved = container.solve(Dependant(controller, scope="request"))
    # this next line would fail without the bind
    async with container.enter_scope("request"):
        await container.execute_async(solved, executor=AsyncExecutor())
    # and we can double check that the bind worked
    # by requesting the instance directly
    async with container.enter_scope("request"):
        db = await container.execute_async(
            container.solve(Dependant(DBProtocol)), executor=AsyncExecutor()
        )
    assert isinstance(db, Postgres)

In this example we register the Postgres class to DBProtocol, and we can see that di auto-wires Postgres as well!

Registration can be used as a direct function call, in which case they are permanent, or as a context manager.