Skip to content

Scope

Scope

Bases: Enum

How long a provider's value lives and when it is rebuilt.

Attributes:

Name Type Description
SINGLETON

Built once, on first resolution, and cached on the FrozenContainer for its whole lifetime. The default. Its lifecycle teardown runs on FrozenContainer.close() / FrozenContainer.aclose(). A singleton may not depend on a scoped provider (it would capture one scope's instance forever) — Container.freeze() rejects that.

SCOPED

Built once per active scope (FrozenContainer.scope() / ascope) and torn down when that scope exits. Resolving one with no active scope raises OutsideScopeError. Typically one scope per request.

TRANSIENT

Built fresh on every resolution and never cached. Generator and context-manager providers cannot be transient.

ScopeFrame

The per-scope store yielded by FrozenContainer.scope().

Holds the scope's cached instances and pending teardowns, and chains to its parent so nested scopes inherit outer instances. Scope-setup code — ASGI middleware, a CLI entry point — uses provide() to seed values that Container.scope_value() then exposes as providers.

A frame is safe to use from several threads and several tasks at once. The root frame caches singletons and is shared by every caller of the container, so its bookkeeping is guarded by a mutex and construction of a given key is single-flighted.

provide

provide(
    key: object, value: object, *, tag: str | None = None
) -> None

Place value into this frame under key.

The counterpart of Container.scope_value(): whoever opens the scope supplies the value, and providers declared with scope_value read it back. Overwrites any value already stored under key in this frame. Seed before anything resolves: a scope_value key is cached in the scope on first use, so a later call here does not replace what a parameter already read.

Example
>>> from depin import Container
>>> class Request: ...
>>> di = Container().scope_value(Request).freeze()
>>> with di.scope() as frame:
...     frame.provide(Request, Request())
...     isinstance(di[Request], Request)
True

get

get(key: object) -> object

Return the value stored under key here or in an ancestor frame.

Raises:

Type Description
KeyError

No frame in the chain holds key.

lookup

lookup(key: object) -> object

Return the value for key, or MISSING when no frame holds it.

Unlike get() this reports absence without raising, so a caller can distinguish "not cached" from "cached as None" in one traversal.

drain_sync

drain_sync() -> None

Run every pending teardown, newest first, without an event loop.

Raises:

Type Description
ExceptionGroup

One or more teardowns failed. Every failure is reported; none is allowed to hide another. Protocol violations, including an async teardown in this synchronous drain, appear as TeardownError members; use drain_async() for async providers.

drain_async async

drain_async() -> None

Run every pending teardown, newest first, inside an event loop.

Raises:

Type Description
ExceptionGroup

One or more teardowns failed.

drain_async_errors async

drain_async_errors() -> tuple[Exception, ...]

Drain pending teardowns and return every failure without grouping it.

drop_sync

drop_sync() -> None

Drain every pending teardown, newest first, and drop the cache, without an event loop.

The counterpart to drain_sync(): where that leaves the cache populated with whatever it just drained, this drops it too, so a key resolved afterwards is rebuilt rather than handed the drained value.

Do not call this while another thread or task may be resolving through this frame: the cache is dropped without coordinating with an in-flight construction, so a resolution racing with the drop can be handed a value whose teardown already ran. FrozenContainer.reset() is this operation on the root frame and carries the same hazard.

Raises:

Type Description
ExceptionGroup

One or more teardowns failed. Every failure is reported, and the cache is dropped either way. Protocol violations, including an async teardown in this synchronous drain, appear as TeardownError members; use drop_async() for async providers.

drop_async async

drop_async() -> None

Drain every pending teardown and drop the cache, inside an event loop.

The counterpart to drop_sync(), for a frame holding an async provider's teardown, and carrying the same hazard: do not call it while another thread or task may be resolving through this frame, because the cache is dropped without coordinating with an in-flight construction.

Raises:

Type Description
ExceptionGroup

One or more teardowns failed. Every failure is reported, and the cache is dropped either way.

claim_cached

claim_cached(
    key: object,
    provided_identity: tuple[object, str | None]
    | None = None,
    *,
    asynchronous: bool = False,
) -> tuple[object, _Flight | _Leader | None]

Atomically return a cached value or claim/join its construction flight.

publish

publish(
    key: object,
    leader: object,
    value: object,
    *,
    signal: bool = False,
) -> _Flight | None

Cache a leader's value and return any followers after unlocking.

abort

abort(
    key: object, leader: object, *, signal: bool = False
) -> _Flight | None

Remove a failed leader's flight and return any followers after unlocking.

start_flight

start_flight(key: object) -> tuple[_Flight | _Leader, bool]

Join a per-key construction flight, or start one when none is active.

finish_flight

finish_flight(
    key: object, flight: _Flight | _Leader
) -> None

Finish flight and remove it only when it remains active for key.