Skip to content

Named Providers

Sometimes you need to register multiple providers for the same type. For example, you might want multiple database connections or different implementations of the same type. You can do this with the Annotated type hint and a string name to tell providers apart.

Basic Usage

from typing import Annotated

from anydi import Container


class DatabaseConnection:
    def __init__(self, host: str, port: int) -> None:
        self.host = host
        self.port = port

    def query(self, sql: str) -> list:
        return []


container = Container()


@container.provider(scope="singleton")
def primary_db() -> Annotated[DatabaseConnection, "primary"]:
    return DatabaseConnection(host="db-primary.local", port=5432)


@container.provider(scope="singleton")
def replica_db() -> Annotated[DatabaseConnection, "replica"]:
    return DatabaseConnection(host="db-replica.local", port=5432)


# Resolve specific providers by name
primary = container.resolve(Annotated[DatabaseConnection, "primary"])
replica = container.resolve(Annotated[DatabaseConnection, "replica"])

assert primary.host == "db-primary.local"
assert replica.host == "db-replica.local"

In this example, we define two providers for different database connections. The Annotated type hint with a string lets you choose which provider to use by the name in the annotation.

Use Cases

Multiple Configurations

from typing import Annotated

from anydi import Container


class APIClient:
    def __init__(self, base_url: str, timeout: int) -> None:
        self.base_url = base_url
        self.timeout = timeout


container = Container()


@container.provider(scope="singleton")
def production_api() -> Annotated[APIClient, "production"]:
    return APIClient(base_url="https://api.production.com", timeout=30)


@container.provider(scope="singleton")
def staging_api() -> Annotated[APIClient, "staging"]:
    return APIClient(base_url="https://api.staging.com", timeout=60)


# Use different API clients based on context
prod_client = container.resolve(Annotated[APIClient, "production"])
staging_client = container.resolve(Annotated[APIClient, "staging"])

Different Implementations

from typing import Annotated, Protocol

from anydi import Container


class StorageBackend(Protocol):
    def save(self, key: str, data: bytes) -> None: ...
    def load(self, key: str) -> bytes: ...


class LocalStorage:
    def save(self, key: str, data: bytes) -> None:
        print(f"Saving to local: {key}")

    def load(self, key: str) -> bytes:
        return b"local_data"


class S3Storage:
    def save(self, key: str, data: bytes) -> None:
        print(f"Saving to S3: {key}")

    def load(self, key: str) -> bytes:
        return b"s3_data"


container = Container()


@container.provider(scope="singleton")
def local_storage() -> Annotated[StorageBackend, "local"]:
    return LocalStorage()


@container.provider(scope="singleton")
def s3_storage() -> Annotated[StorageBackend, "s3"]:
    return S3Storage()


# Choose storage backend based on requirements
local = container.resolve(Annotated[StorageBackend, "local"])
cloud = container.resolve(Annotated[StorageBackend, "s3"])

Related Topics: - Provider Basics - Learn basic provider registration - Testing - Learn how to override providers for testing - Dependency Injection - Learn how to inject named providers