"""One process-wide binding with a context-scoped override.A :class:`~contextvars.ContextVar` is the right shape for a value that muststay private to one span of work, and the wrong shape for a value that nameswhat is true of the whole process. The live profile session is the secondkind: exactly one profile is logged in per process, and every entry point --a parsed CLI invocation, a Textual thread worker, a second:func:`asyncio.run` frame in the same program -- must observe the sameanswer.PEP 567 semantics defeat that on their own. ``asyncio.run`` runs its loop ina fresh context, a thread does not inherit its spawner's context, and:meth:`contextvars.Context.run` discards every write made inside it. Abinding installed in any of those frames is therefore invisible to the framethat reads it, which presents to the operator as a correct login followed bya surface that cannot find the profile it just authenticated.This binding composes both needs. The process-wide value is guarded by alock and is what an unscoped ``bind`` publishes; the context-scoped overrideis what a ``with`` block installs for the duration of one span and unwinds onexit. Reads consult the override first, so a nested span still shadows theprocess value exactly as a plain ``ContextVar`` did, and fall back to theprocess value when no override is in force.The binding holds a reference, never a copy: an owner that zeroises keymaterial on close keeps that responsibility, and this type only decides whocan see the object."""from__future__importannotationsimportthreadingfromcollections.abcimportGeneratorfromcontextlibimportcontextmanagerfromcontextvarsimportContextVarfromtypingimportfinal
[docs]@finalclassProcessScopedBinding[T]:"""Publish one value process-wide while allowing a scoped override. The override is stored as a one-tuple rather than the bare value so that "no override in this context" and "an override deliberately set to ``None``" stay distinguishable. The second is a real state: a caller that suspends the binding for a block is asserting an absence, and collapsing it into the first would let the process value show through the very block that hid it. """__slots__=("_lock","_override","_value")def__init__(self,name:str)->None:"""Create an unbound binding whose override variable is named ``name``."""self._override:ContextVar[tuple[T|None]|None]=ContextVar(name,default=None)self._lock=threading.Lock()self._value:T|None=None
[docs]defget(self)->T|None:"""Return the override in force for this context, else the process value."""override=self._override.get()ifoverrideisnotNone:returnoverride[0]withself._lock:returnself._value
[docs]defbind(self,value:T|None)->None:"""Publish ``value`` process-wide, and into any override in force here. Refreshing an override in force is what keeps an unscoped bind truthful to its own caller: a bind performed inside a span that shadows the process value would otherwise be published to every context except the one that performed it. """withself._lock:self._value=valueifself._override.get()isnotNone:self._override.set((value,))
[docs]defclear_bound(self,expected:T)->None:"""Remove ``expected`` wherever this context can still observe it. A retiring owner must unbind the exact object it observed, from the override in force here as well as from the process value, because a session closed inside an overriding block is bound in both places and clearing only one leaves a zeroised object advertised as live. Identity, not equality: a replacement installed reentrantly during the retirement is a different object and must survive it, which is exactly what an equality comparison would fail to preserve. """override=self._override.get()ifoverrideisnotNoneandoverride[0]isexpected:self._override.set((None,))withself._lock:ifself._valueisexpected:self._value=None
[docs]@contextmanagerdefoverride(self,value:T|None)->Generator[None]:"""Shadow the process value with ``value`` for the duration of the block. Overrides stack and unwind through the :class:`~contextvars.Token` the override variable returns, so a nested block restores the exact binding its caller observed. The process value is never touched here; only :meth:`bind` and :meth:`clear_bound` change what the rest of the program sees. """token=self._override.set((value,))try:yieldfinally:self._override.reset(token)