A scope that can be closed. Use through the Scope tag.
Finalizers are keyed, so whatever registered one can take it back when
it no longer needs it: a child scope leaves its parent when it closes,
and a fork_scoped fiber leaves its scope when it exits. A
"parallel" scope runs its finalizers concurrently on close.
Attributes#
| Attribute | Type | Description |
|---|---|---|
finalizer_count | int | How many finalizers are registered and not yet run. |
is_empty | bool | Open with no finalizers, so it can be handed on for reuse
(Effect's |
is_closed | bool | Whether |
Functions#
__init__#
__init__(strategy='sequential') -> None| Parameter | Type | Description |
|---|---|---|
strategy | Strategy = 'sequential' |
def __init__(self, strategy: Strategy = "sequential") -> None:
self._strategy: Strategy = strategy
self._finalizers: dict[object, Finalizer] = {}
self._closed: Exit[Any, Any] | None = None
self._parent: CloseableScope | None = Noneadd_finalizer#
add_finalizer(f) -> Effect[None]Register f to run on close, with the services in scope now.
If the scope is already closed, f runs immediately.
| Parameter | Type | Description |
|---|---|---|
f | Callable[[Exit[Any, Any]], Effect[object, Never, Any]] |
Returns Effect[None]
def add_finalizer(
self, f: Callable[[Exit[Any, Any]], Effect[object, Never, Any]]
) -> Effect[None]:
"""Register ``f`` to run on close, with the services in scope now.
If the scope is already closed, ``f`` runs immediately."""
def body(fiber: Fiber[Any, Any]) -> Effect[None]:
ctx = fiber._context
# As Effect's ``provideContext``: the closing fiber's services,
# overridden by those in scope at registration.
def finalizer(ex: Exit[Any, Any]) -> Effect[object, Never, Any]:
return _Locally(_call_user(f, ex), lambda current: current.merge(ctx))
if self._closed is not None:
return finalizer(self._closed).as_void()
self._add(object(), finalizer)
return _Succeed(None)
return _WithFiber(body)close#
close(ex) -> Effect[None]Run every finalizer, last added first (all at once in a parallel scope). Closing twice is a no-op.
| Parameter | Type | Description |
|---|---|---|
ex | Exit[Any, Any] |
Returns Effect[None]
def close(self, ex: Exit[Any, Any]) -> Effect[None]:
"""Run every finalizer, last added first (all at once in a parallel
scope). Closing twice is a no-op."""
# Uninterruptible before the scope is marked closed, so an interrupt
# can't land between marking it and running its finalizers.
return _Suspend(lambda: self.close_unsafe(ex) or _Succeed(None)).uninterruptible()close_unsafe#
close_unsafe(ex) -> Effect[None] | NoneMark the scope closed now and return the effect that runs its
finalizers, or None if it was closed already or had none. The
caller must run the effect uninterruptibly (Effect's closeUnsafe).
| Parameter | Type | Description |
|---|---|---|
ex | Exit[Any, Any] |
Returns Effect[None] | None
def close_unsafe(self, ex: Exit[Any, Any]) -> Effect[None] | None:
"""Mark the scope closed now and return the effect that runs its
finalizers, or ``None`` if it was closed already or had none. The
caller must run the effect uninterruptibly (Effect's ``closeUnsafe``)."""
if self._closed is not None:
return None
self._closed = ex
if self._parent is not None:
self._parent._remove(self)
if not self._finalizers:
return None
entries = list(reversed(self._finalizers.items()))
self._finalizers = {}
run = _run_parallel if self._strategy == "parallel" else _run_sequential
return run(entries, ex)fork#
fork(strategy='sequential') -> Effect[CloseableScope]A child scope, closed when this one closes (if not before). The child of a closed scope starts closed.
| Parameter | Type | Description |
|---|---|---|
strategy | Strategy = 'sequential' |
Returns Effect[CloseableScope]
def fork(self, strategy: Strategy = "sequential") -> Effect[CloseableScope]:
"""A child scope, closed when this one closes (if not before). The
child of a closed scope starts closed."""
return _Sync(lambda: self._fork_now(strategy))