Function injection¶
What you’ll learn¶
Use
Injected[T]to mark injected parameters.Wrap callables with
@resolver_context.inject(...).Control scope behavior for injected wrappers.
Signature filtering¶
Run locally¶
uv run python examples/ex_06_function_injection/01_signature_filtering.py
"""Focused example: ``Injected[T]`` and public signature filtering."""
from __future__ import annotations
import inspect
from dataclasses import dataclass
from diwire import Container, Injected, resolver_context
@dataclass(slots=True)
class User:
email: str
def main() -> None:
container = Container()
container.add_instance(User(email="user@example.com"))
@resolver_context.inject
def handler(user_email: str, user: Injected[User], user_name: str) -> str:
return f"{user_email}|{user_name}|{user.email}"
signature = "('" + "','".join(inspect.signature(handler).parameters) + "')"
print(f"signature={signature}") # => signature=('user_email','user_name')
if __name__ == "__main__":
main()
Override injected values¶
Run locally¶
uv run python examples/ex_06_function_injection/02_override_injected.py
"""Focused example: caller override for injected parameters."""
from __future__ import annotations
from dataclasses import dataclass
from diwire import Container, Injected, resolver_context
@dataclass(slots=True)
class User:
email: str
def main() -> None:
container = Container()
container.add_instance(User(email="container@example.com"))
@resolver_context.inject
def handler(user: Injected[User]) -> str:
return user.email
default_value = handler()
override_value = handler(user=User(email="override@example.com"))
print(f"default_injected={default_value}") # => default_injected=container@example.com
print(f"override_injected={override_value}") # => override_injected=override@example.com
if __name__ == "__main__":
main()
Auto-open scope cleanup¶
Run locally¶
uv run python examples/ex_06_function_injection/03_auto_open_scope_cleanup.py
"""Focused example: ``auto_open_scope`` with scoped cleanup."""
from __future__ import annotations
from collections.abc import Generator
from diwire import Container, Injected, Lifetime, Scope, resolver_context
class Resource:
pass
def main() -> None:
container = Container()
state = {"cleaned": False}
def provide_resource() -> Generator[Resource, None, None]:
try:
yield Resource()
finally:
state["cleaned"] = True
container.add_generator(
provide_resource,
provides=Resource,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
@resolver_context.inject(scope=Scope.REQUEST, auto_open_scope=True)
def handler(resource: Injected[Resource]) -> Resource:
return resource
_ = handler()
print(f"auto_scope_cleanup={state['cleaned']}") # => auto_scope_cleanup=True
if __name__ == "__main__":
main()
Nested wrappers¶
Run locally¶
uv run python examples/ex_06_function_injection/04_nested_wrappers.py
"""Focused example: nested injected wrappers share one active resolver."""
from __future__ import annotations
from dataclasses import dataclass
from diwire import Container, Injected, Lifetime, Scope, resolver_context
class RequestDependency:
pass
@dataclass(slots=True)
class InnerService:
dependency: RequestDependency
@dataclass(slots=True)
class OuterService:
inner: InnerService
dependency: RequestDependency
def main() -> None:
container = Container()
container.add(
RequestDependency,
provides=RequestDependency,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
@resolver_context.inject
def build_inner(dependency: Injected[RequestDependency]) -> InnerService:
return InnerService(dependency=dependency)
@resolver_context.inject
def build_outer(
inner: Injected[InnerService],
dependency: Injected[RequestDependency],
) -> OuterService:
return OuterService(inner=inner, dependency=dependency)
container.add_factory(
build_inner,
provides=InnerService,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
container.add_factory(
build_outer,
provides=OuterService,
scope=Scope.REQUEST,
lifetime=Lifetime.TRANSIENT,
)
with container.enter_scope() as request_scope:
resolved = request_scope.resolve(OuterService)
print(
f"nested_scope_identity={resolved.inner.dependency is resolved.dependency}",
) # => nested_scope_identity=True
if __name__ == "__main__":
main()
Auto-open scope reuse¶
Run locally¶
uv run python examples/ex_06_function_injection/05_auto_open_scope_reuse.py
"""Focused example: auto-open scope reuses already-open resolvers."""
from __future__ import annotations
from collections.abc import Generator
from contextvars import ContextVar
from diwire import Container, Injected, Lifetime, Scope, resolver_context
class RequestResource:
pass
current_value_var: ContextVar[int] = ContextVar("auto_open_scope_reuse_value", default=0)
def main() -> None:
container = Container()
cleanup_state = {"cleaned": False}
def provide_request_resource() -> Generator[RequestResource, None, None]:
try:
yield RequestResource()
finally:
cleanup_state["cleaned"] = True
def provide_current_value() -> int:
return current_value_var.get()
container.add_generator(
provide_request_resource,
provides=RequestResource,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
container.add_factory(provide_current_value, provides=int, scope=Scope.SESSION)
@resolver_context.inject(scope=Scope.REQUEST, auto_open_scope=True)
def use_request_resource(resource: Injected[RequestResource]) -> RequestResource:
return resource
with container.enter_scope(Scope.REQUEST) as request_scope:
resolved_resource = use_request_resource(diwire_resolver=request_scope)
print(
f"target_scope_reused={isinstance(resolved_resource, RequestResource) and not cleanup_state['cleaned']}",
) # => target_scope_reused=True
print(
f"cleanup_after_outer_scope={cleanup_state['cleaned']}"
) # => cleanup_after_outer_scope=True
@resolver_context.inject(scope=Scope.SESSION, auto_open_scope=True)
def read_value(value: Injected[int]) -> int:
return value
with (
container.enter_scope(Scope.SESSION) as session_scope,
session_scope.enter_scope(Scope.REQUEST) as request_scope,
):
token = current_value_var.set(22)
try:
resolved_value = read_value(diwire_resolver=request_scope)
finally:
current_value_var.reset(token)
print(
f"deeper_scope_contextvar_reused={resolved_value}"
) # => deeper_scope_contextvar_reused=22
if __name__ == "__main__":
main()
Async deep dive¶
Run locally¶
uv run python examples/ex_06_function_injection/06_function_injection_async_details.py
"""Async function-injection deep dive.
This focused script covers async callables using ``Injected[T]`` and caller
overrides for injected async parameters.
"""
from __future__ import annotations
import asyncio
from dataclasses import dataclass
from diwire import Container, Injected, resolver_context
@dataclass(slots=True)
class AsyncUser:
email: str
async def main() -> None:
container = Container()
container.add_instance(AsyncUser(email="async@example.com"))
@resolver_context.inject
async def handler(user: Injected[AsyncUser]) -> str:
return user.email
default_value = await handler()
overridden_value = await handler(user=AsyncUser(email="override@example.com"))
if default_value != "async@example.com":
msg = "Unexpected default async injection result"
raise TypeError(msg)
if overridden_value != "override@example.com":
msg = "Unexpected async override injection result"
raise TypeError(msg)
if __name__ == "__main__":
asyncio.run(main())