Qualifiers & overrides¶
Two small tools:
Qualifyto disambiguate duplicate types, andOverridesto 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).