Scope context values¶
What you’ll learn¶
Pass per-scope values via
enter_scope(..., context={...}).Resolve context values via
FromContext[T]in providers and injected callables.
Provider from context¶
Run locally¶
uv run python examples/ex_17_scope_context_values/01_provider_from_context.py
"""Focused example: provider dependencies can read ``FromContext[T]`` values."""
from __future__ import annotations
from diwire import Container, FromContext, Lifetime, Scope
class RequestValue:
def __init__(self, value: int) -> None:
self.value = value
def build_request_value(value: FromContext[int]) -> RequestValue:
return RequestValue(value=value)
def main() -> None:
container = Container()
container.add_factory(
build_request_value,
provides=RequestValue,
scope=Scope.REQUEST,
lifetime=Lifetime.TRANSIENT,
)
with container.enter_scope(Scope.REQUEST, context={int: 7}) as request_scope:
resolved = request_scope.resolve(RequestValue)
print(f"provider_from_context={resolved.value}") # => provider_from_context=7
if __name__ == "__main__":
main()
Nested scope inheritance¶
Run locally¶
uv run python examples/ex_17_scope_context_values/02_nested_scope_inheritance.py
"""Focused example: nested scopes inherit context and child scopes can override keys."""
from __future__ import annotations
from diwire import Container, FromContext, Scope
def main() -> None:
container = Container()
with (
container.enter_scope(Scope.REQUEST, context={int: 1, str: "parent"}) as request_scope,
request_scope.enter_scope(Scope.ACTION) as action_scope,
action_scope.enter_scope(Scope.STEP, context={int: 2}) as step_scope,
):
inherited_value = action_scope.resolve(FromContext[int])
overridden_value = step_scope.resolve(FromContext[int])
inherited_parent_key = step_scope.resolve(FromContext[str])
print(f"action_inherits_parent={inherited_value}") # => action_inherits_parent=1
print(f"step_overrides_parent={overridden_value}") # => step_overrides_parent=2
print(
f"step_inherits_other_parent_key={inherited_parent_key}"
) # => step_inherits_other_parent_key=parent
if __name__ == "__main__":
main()
Injected callable context¶
Run locally¶
uv run python examples/ex_17_scope_context_values/03_injected_callable_context.py
"""Injected callables can consume FromContext values via diwire_context."""
from __future__ import annotations
from diwire import Container, FromContext, Scope, resolver_context
def main() -> None:
Container()
@resolver_context.inject(scope=Scope.REQUEST)
def handler(value: FromContext[int]) -> int:
return value
from_context = handler(diwire_context={int: 7})
overridden = handler(value=8)
print(f"from_context={from_context}") # => from_context=7
print(f"overridden={overridden}") # => overridden=8
if __name__ == "__main__":
main()
Annotated context keys¶
Run locally¶
uv run python examples/ex_17_scope_context_values/04_annotated_context_keys.py
"""Annotated tokens can be used as scope context keys."""
from __future__ import annotations
from typing import Annotated, TypeAlias
from diwire import Component, Container, FromContext, Lifetime, Scope
ReplicaNumber: TypeAlias = Annotated[int, Component("replica")]
class ReplicaConsumer:
def __init__(self, value: int) -> None:
self.value = value
def build_consumer(value: FromContext[ReplicaNumber]) -> ReplicaConsumer:
return ReplicaConsumer(value=value)
def main() -> None:
container = Container()
container.add_factory(
build_consumer,
provides=ReplicaConsumer,
scope=Scope.REQUEST,
lifetime=Lifetime.TRANSIENT,
)
with container.enter_scope(Scope.REQUEST, context={ReplicaNumber: 42}) as request_scope:
resolved = request_scope.resolve(ReplicaConsumer)
direct = request_scope.resolve(FromContext[ReplicaNumber])
print(f"consumer_value={resolved.value}") # => consumer_value=42
print(f"direct_value={direct}") # => direct_value=42
if __name__ == "__main__":
main()
Context without scope open¶
Run locally¶
uv run python examples/ex_17_scope_context_values/05_context_without_scope_open.py
"""Passing diwire_context without opening a scope raises a clear error."""
from __future__ import annotations
from diwire import Container, FromContext, resolver_context
from diwire.exceptions import DIWireInvalidRegistrationError
def main() -> None:
Container()
@resolver_context.inject(auto_open_scope=False)
def handler(value: FromContext[int]) -> int:
return value
try:
handler(diwire_context={int: 7})
except DIWireInvalidRegistrationError as error:
error_name = type(error).__name__
print(
f"context_without_scope_error={error_name}"
) # => context_without_scope_error=DIWireInvalidRegistrationError
if __name__ == "__main__":
main()