Skip to content

Qualifiers & overrides

Two small tools: Qualify to disambiguate duplicate types, and Overrides to substitute bindings in tests without touching globals.

Qualifiers

When two bindings produce the same type — a primary and a replica database — a bare type can't tell them apart. Tag each with a qualifier: bind the alternate with qualifier='replica', and request it with Annotated[Database, Qualify('replica')] in a recipe or route parameter. The unqualified binding stays the default.

from typing import Annotated

from gazebo.di import Providers, Qualify


class Database:
    def __init__(self, role: str) -> None:
        self.role = role


providers = Providers()
providers.app(Database, lambda: Database('primary'))  # the default
providers.app(Database, lambda: Database('replica'), qualifier='replica')  # disambiguated


class Catalog:
    db: Database

    @classmethod
    def __provide__(cls, db: Annotated[Database, Qualify('replica')]) -> Catalog:
        catalog = cls()
        catalog.db = db  # resolves the 'replica' binding
        return catalog

Overrides

Overrides is mechanically a thin Providers layer: .set(T, value) replaces a binding's recipe — or supplies a constant instance — keeping the original scope. It's type-checked (.set(Settings, 5) is a type error) and errors if you override a key that was never bound. Pass it at app construction, so a test swaps real config or resources for fakes by parameter — never by mutating a global, which keeps tests isolated and parallel-safe.

from gazebo.di import Container, Overrides

# In a test: replace a binding by parameter, never by mutating a global.
overrides = Overrides().set(Database, Database('in-memory'), qualifier='replica')
container = Container(providers, overrides=overrides)

Reference

See gazebo.di.providers (Qualify, Overrides).