Scopes & cleanup¶
Scopes give you a way to say: “for this unit of work (request/job), reuse scoped services and clean them up at the end.”
Scope model¶
Scope is an ordered scope tree implemented as a collection of diwire.BaseScope instances.
Each scope has:
a numeric
level(depth)a
skippableflag used for default transitions
The built-in scope collection is available as diwire.Scope (an instance, not an enum).
Entering scopes¶
Use diwire.Container.enter_scope() as a context manager.
enter_scope() (no argument) transitions to the next non-skippable scope level.
With the built-in tree this means APP -> REQUEST by skipping SESSION.
You can also enter an explicit scope level with enter_scope(Scope.SESSION) or enter_scope(Scope.REQUEST).
Runnable example: Scopes & cleanup.
Scoped lifetime¶
To cache a provider within a scope, register it as Lifetime.SCOPED and set scope=... to a
diwire.BaseScope value:
from diwire import Container, Lifetime, Scope
class RequestSession: ...
container = Container()
container.add(RequestSession, provides=RequestSession,
scope=Scope.REQUEST,
lifetime=Lifetime.SCOPED,
)
Cleanup¶
For deterministic cleanup, use generator / async-generator providers (or context manager providers). diwire will run
cleanup when the owning scope exits.
Generator registrations added via add_generator() are validated by default and must place yield in a
try/finally block. Opt out per registration with require_generator_finally=False when intentionally needed.
Runnable example: Scopes & cleanup.
Task-local values with ContextVar¶
For request/task-local data (tenant id, auth claims, request id), use contextvars.ContextVar and
register a normal provider that reads ContextVar.get().
Runnable examples: Scope context values.