"""Build-time context for configuration construction.A :class:`BuildContext` is set on a :class:`~contextvars.ContextVar` while aconfiguration tree is being built and cleared once it finalizes. Code runningunderneath the build (validators, ``required=`` callables, ...) can retrieve itvia :func:`get_config_build_context` to share state across nodes withoutthreading it through every constructor.The context is a namespace: callers attach packages' shared objects under asub-namespace, e.g. ``ctx.bsb_nest.kernel = proxy``. Resources that needteardown register a callback via :meth:`BuildContext.add_cleanup`; callbacksrun in LIFO order when the build exits."""importcontextlibfromcontextvarsimportContextVarfromtypesimportSimpleNamespace
[docs]classBuildContext(SimpleNamespace):"""Per-build shared state, accessed via attribute-style sub-namespaces. Reading a top-level attribute that hasn't been set yet auto-creates an empty ``SimpleNamespace`` sub-namespace, so callers can write ``ctx.bsb_nest.kernel = proxy`` without manually setting up ``ctx.bsb_nest`` first. Leaf reads (``ctx.bsb_nest.kernel``) are NOT auto-vivified — callers must use ``ns.__dict__.get(...)`` or ``getattr(ns, ..., default)`` for missing-leaf checks. """def__init__(self):super().__init__()object.__setattr__(self,"_cleanup_callbacks",[])def__getattr__(self,name):ifname.startswith("_"):raiseAttributeError(name)ns=SimpleNamespace()object.__setattr__(self,name,ns)returnns
[docs]defadd_cleanup(self,callback):"""Register a zero-arg callable to run when the build context exits."""self._cleanup_callbacks.append(callback)
def_run_cleanups(self):whileself._cleanup_callbacks:cb=self._cleanup_callbacks.pop()# A cleanup failure must not prevent later cleanups from running.withcontextlib.suppress(Exception):cb()
[docs]defset_config_build_context(ctx):"""Set the active build context. Returns the reset token."""return_build_context_var.set(ctx)
[docs]defget_config_build_context():"""Return the active :class:`BuildContext` or ``None`` outside a build."""return_build_context_var.get()
[docs]@contextlib.contextmanagerdefbuild_context():"""Context manager that owns the lifecycle of a :class:`BuildContext`."""ctx=BuildContext()token=set_config_build_context(ctx)try:yieldctxfinally:try:ctx._run_cleanups()finally:_build_context_var.reset(token)