Open generics¶
What you’ll learn¶
Register open-generic providers and resolve closed generic keys.
Understand precedence and validation behavior.
Factory type argument¶
Run locally¶
uv run python examples/ex_08_open_generics/01_factory_type_argument.py
"""Focused example: open generic factory with ``type[T]`` injection."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar, cast
from diwire import Container
T = TypeVar("T")
class IBox(Generic[T]):
pass
@dataclass(slots=True)
class Box(IBox[T]):
type_arg: type[T]
def build_box(type_arg: type[T]) -> IBox[T]:
return Box(type_arg=type_arg)
def main() -> None:
container = Container()
container.add_factory(build_box, provides=IBox)
resolved = cast("Box[int]", container.resolve(IBox[int]))
print(f"box_int={resolved.type_arg.__name__}") # => box_int=int
if __name__ == "__main__":
main()
Closed override¶
Run locally¶
uv run python examples/ex_08_open_generics/02_closed_override.py
"""Focused example: closed generic override beats open template."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar
from diwire import Container
T = TypeVar("T")
class IBox(Generic[T]):
pass
@dataclass(slots=True)
class Box(IBox[T]):
type_arg: type[T]
class _SpecialIntBox(IBox[int]):
pass
def main() -> None:
container = Container()
container.add(Box, provides=IBox)
container.add(_SpecialIntBox, provides=IBox[int])
resolved = container.resolve(IBox[int])
print(f"override={type(resolved).__name__}") # => override=_SpecialIntBox
if __name__ == "__main__":
main()
Specificity winner¶
Run locally¶
uv run python examples/ex_08_open_generics/03_specificity_winner.py
"""Focused example: most-specific open template selection."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar, cast
from diwire import Container
T = TypeVar("T")
U = TypeVar("U")
class Repo(Generic[T]):
pass
@dataclass(slots=True)
class GenericRepo(Repo[T]):
dependency_type: type[T]
@dataclass(slots=True)
class ListRepo(Repo[list[U]]):
item_type: type[U]
def main() -> None:
container = Container()
container.add(GenericRepo, provides=Repo)
container.add(ListRepo, provides=Repo[list[U]])
resolved = cast("ListRepo[int]", container.resolve(Repo[list[int]]))
print(f"specificity_item={resolved.item_type.__name__}") # => specificity_item=int
if __name__ == "__main__":
main()
Scoped open generics¶
Run locally¶
uv run python examples/ex_08_open_generics/04_scoped_open_generics.py
"""Focused example: scoped open generics require an opened scope."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar
from diwire import Container, Lifetime, Scope
from diwire.exceptions import DIWireScopeMismatchError
T = TypeVar("T")
class IBox(Generic[T]):
pass
@dataclass(slots=True)
class Box(IBox[T]):
type_arg: type[T]
def build_box(type_arg: type[T]) -> IBox[T]:
return Box(type_arg=type_arg)
def main() -> None:
container = Container()
container.add_factory(
build_box,
provides=IBox,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
try:
container.resolve(IBox[int])
except DIWireScopeMismatchError:
requires_scope = True
else:
requires_scope = False
print(f"scoped_requires_scope={requires_scope}") # => scoped_requires_scope=True
if __name__ == "__main__":
main()
Constraint details¶
Run locally¶
uv run python examples/ex_08_open_generics/05_open_generics_constraints_details.py
"""Open-generics constraints deep dive.
This focused script shows constrained ``TypeVar`` behavior with both valid and
invalid resolutions.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Generic, TypeVar, cast
from diwire import Container
from diwire.exceptions import DIWireInvalidGenericTypeArgumentError
Allowed = TypeVar("Allowed", int, str)
class ConstrainedBox(Generic[Allowed]):
pass
@dataclass(slots=True)
class ConstrainedBoxImpl(ConstrainedBox[Allowed]):
type_arg: type[Allowed]
def main() -> None:
container = Container()
container.add(ConstrainedBoxImpl, provides=ConstrainedBox)
valid_int = container.resolve(ConstrainedBox[int])
valid_str = container.resolve(ConstrainedBox[str])
if cast("ConstrainedBoxImpl[int]", valid_int).type_arg is not int:
msg = "Expected int constrained type argument"
raise TypeError(msg)
if cast("ConstrainedBoxImpl[str]", valid_str).type_arg is not str:
msg = "Expected str constrained type argument"
raise TypeError(msg)
invalid_key = cast("Any", ConstrainedBox)[float]
try:
container.resolve(invalid_key)
except DIWireInvalidGenericTypeArgumentError:
return
msg = "Expected DIWireInvalidGenericTypeArgumentError for constrained TypeVar"
raise TypeError(msg)
if __name__ == "__main__":
main()