Scopes¶
Scope basics¶
Demonstrates how to use scopes with enter_scope().
Scopes allow grouping related service resolutions together.
from enum import Enum
from diwire import Container
class Scope(str, Enum):
"""Application scope definitions."""
REQUEST = "request"
class RequestContext:
"""Holds request-specific data."""
def __init__(self) -> None:
self.request_id = id(self)
def __repr__(self) -> str:
return f"RequestContext(request_id={self.request_id})"
def main() -> None:
container = Container()
container.register(RequestContext)
# Using enter_scope() with an Enum value
print("Scope usage with context manager:\n")
with container.enter_scope(Scope.REQUEST) as scope:
# Resolve services within the scope
ctx1 = scope.resolve(RequestContext)
ctx2 = scope.resolve(RequestContext)
print(f"Inside scope '{Scope.REQUEST.value}':")
print(f" ctx1: {ctx1}")
print(f" ctx2: {ctx2}")
print(f" Same instance (transient): {ctx1 is ctx2}")
# Each scope is independent
print("\nMultiple independent scopes:")
with container.enter_scope(Scope.REQUEST) as scope1:
ctx_a = scope1.resolve(RequestContext)
print(f" Scope 1 context: {ctx_a}")
with container.enter_scope(Scope.REQUEST) as scope2:
ctx_b = scope2.resolve(RequestContext)
print(f" Scope 2 context: {ctx_b}")
print(f" Different instances: {ctx_a is not ctx_b}")
if __name__ == "__main__":
main()
Nested scopes¶
Demonstrates hierarchical scope nesting where child scopes can access services from parent scopes.
from enum import Enum
from diwire import Container, Lifetime
class Scope(str, Enum):
"""Application scope definitions."""
REQUEST = "request"
HANDLER = "handler"
class RequestContext:
"""Request-level context, shared within a request."""
def __init__(self) -> None:
self.request_id = id(self)
class HandlerContext:
"""Handler-level context, specific to each handler invocation."""
def __init__(self) -> None:
self.handler_id = id(self)
def main() -> None:
container = Container()
container.register(
RequestContext,
lifetime=Lifetime.SCOPED,
scope=Scope.REQUEST,
)
container.register(
HandlerContext,
lifetime=Lifetime.SCOPED,
scope=Scope.HANDLER,
)
print("Nested scopes demonstration:\n")
with container.enter_scope(Scope.REQUEST) as request_scope:
request_ctx = request_scope.resolve(RequestContext)
print(f"Request scope - RequestContext id: {request_ctx.request_id}")
# Create nested handler scopes
with request_scope.enter_scope(Scope.HANDLER) as handler_scope1:
handler_ctx1 = handler_scope1.resolve(HandlerContext)
# Parent's RequestContext is accessible from child
inherited_request_ctx = handler_scope1.resolve(RequestContext)
print("\n Handler scope 1:")
print(f" HandlerContext id: {handler_ctx1.handler_id}")
print(f" RequestContext id: {inherited_request_ctx.request_id}")
print(f" Same request context: {inherited_request_ctx is request_ctx}")
with request_scope.enter_scope(Scope.HANDLER) as handler_scope2:
handler_ctx2 = handler_scope2.resolve(HandlerContext)
inherited_request_ctx2 = handler_scope2.resolve(RequestContext)
print("\n Handler scope 2:")
print(f" HandlerContext id: {handler_ctx2.handler_id}")
print(f" RequestContext id: {inherited_request_ctx2.request_id}")
print(f" Same request context: {inherited_request_ctx2 is request_ctx}")
print(f" Different handler context: {handler_ctx2 is not handler_ctx1}")
if __name__ == "__main__":
main()
Generator factories (cleanup on scope exit)¶
Demonstrates generator factory support:
the yielded instance is used by the container
the generator is closed on scope exit
from __future__ import annotations
from collections.abc import Generator
from enum import Enum
from diwire import Container, Lifetime
class Scope(str, Enum):
"""Application scope definitions."""
REQUEST = "request"
class Session:
"""A session that needs cleanup when the scope ends."""
def __init__(self) -> None:
self.session_id = id(self)
self.closed = False
def close(self) -> None:
self.closed = True
print(f"Session {self.session_id} closed")
def __repr__(self) -> str:
return f"Session(id={self.session_id}, closed={self.closed})"
def session_factory() -> Generator[Session, None, None]:
session = Session()
print(f"Session {session.session_id} opened")
try:
yield session
finally:
session.close()
def main() -> None:
container = Container()
container.register(
Session,
factory=session_factory,
lifetime=Lifetime.SCOPED,
scope=Scope.REQUEST,
)
print("Generator factory scope behavior:\n")
with container.enter_scope(Scope.REQUEST):
session1 = container.resolve(Session)
session2 = container.resolve(Session)
print(f"session1: {session1}")
print(f"session2: {session2}")
print(f"Same instance: {session1 is session2}")
print("\nScope exited; generator cleanup should have run.")
if __name__ == "__main__":
main()