Coverage for src/lilbee/core/config/context.py: 100%
14 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 11:46 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-14 11:46 +0000
1"""Per-scope active config: how the library API runs against its own Config.
3The process-global ``cfg`` singleton backs the CLI, TUI, and HTTP daemon. The
4library API (:class:`lilbee.Lilbee`) instead binds a caller-supplied Config for
5the duration of each public method via :func:`config_scope`, and the ingest path
6reads it through :func:`active_config` instead of the global. Scoping is a
7ContextVar, so it stays isolated to the entering task and propagates into the
8``to_ingest_thread`` workers (they copy the calling context), without mutating
9the process-global cfg that other clients share.
10"""
12from __future__ import annotations
14from contextlib import contextmanager
15from contextvars import ContextVar
16from typing import TYPE_CHECKING
18from lilbee.core.config.model import cfg
20if TYPE_CHECKING:
21 from collections.abc import Iterator
23 from lilbee.core.config.model import Config
25_active: ContextVar[Config | None] = ContextVar("lilbee_active_config", default=None)
28def active_config() -> Config:
29 """Return the scoped Config if one is active, else the process-global ``cfg``."""
30 return _active.get() or cfg
33@contextmanager
34def config_scope(config: Config) -> Iterator[None]:
35 """Bind *config* as the active config for the duration of the block."""
36 token = _active.set(config)
37 try:
38 yield
39 finally:
40 _active.reset(token)