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

1"""Per-scope active config: how the library API runs against its own Config. 

2 

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""" 

11 

12from __future__ import annotations 

13 

14from contextlib import contextmanager 

15from contextvars import ContextVar 

16from typing import TYPE_CHECKING 

17 

18from lilbee.core.config.model import cfg 

19 

20if TYPE_CHECKING: 

21 from collections.abc import Iterator 

22 

23 from lilbee.core.config.model import Config 

24 

25_active: ContextVar[Config | None] = ContextVar("lilbee_active_config", default=None) 

26 

27 

28def active_config() -> Config: 

29 """Return the scoped Config if one is active, else the process-global ``cfg``.""" 

30 return _active.get() or cfg 

31 

32 

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)