Skip to content

config_stack

config_stack

Generic layered config resolution.

Domain-agnostic: no terok service dependencies.

Terminology

  • Scope: a single config layer (e.g. "global", "project", "preset", "cli").
  • Stack: an ordered list of scopes, lowest-priority first.
  • deep_merge: recursive dict merge with _inherit support.

ConfigScope(level, source, data) dataclass

A single layer in the config stack.

ConfigStack()

Ordered collection of config scopes, lowest-priority first.

Usage::

stack = ConfigStack()
stack.push(ConfigScope("global", global_path, global_data))
stack.push(ConfigScope("project", proj_path, proj_data))
resolved = stack.resolve()

Initialise an empty config stack.

Source code in src/terok_agent/config_stack.py
def __init__(self) -> None:
    """Initialise an empty config stack."""
    self._scopes: list[ConfigScope] = []

scopes property

Read-only access to the scope list (for diagnostics).

push(scope)

Append a scope (higher priority than all previous).

Source code in src/terok_agent/config_stack.py
def push(self, scope: ConfigScope) -> None:
    """Append a scope (higher priority than all previous)."""
    self._scopes.append(scope)

resolve()

Deep-merge all scopes in order and return the result.

Source code in src/terok_agent/config_stack.py
def resolve(self) -> dict:
    """Deep-merge all scopes in order and return the result."""
    result: dict = {}
    for scope in self._scopes:
        result = deep_merge(result, scope.data)
    return result

resolve_section(key)

Resolve only a single top-level section across all scopes.

Source code in src/terok_agent/config_stack.py
def resolve_section(self, key: str) -> dict:
    """Resolve only a single top-level section across all scopes."""
    result: dict = {}
    for scope in self._scopes:
        section = scope.data.get(key)
        if isinstance(section, dict):
            result = deep_merge(result, section)
    return result

deep_merge(base, override)

Recursively merge override into base, returning a new dict.

Rules
  • Dicts are merged recursively by default.
  • A None value in override deletes the corresponding key.
  • A bare "_inherit" string keeps the base value unchanged (equivalent to omitting the key, but explicit).
  • Lists in override replace the base list wholesale unless the list contains the sentinel string "_inherit", in which case the sentinel is replaced by the base list elements (splice).
  • A dict in override that contains _inherit: true keeps all parent keys and overlays the rest (the _inherit key itself is stripped from the result).
Source code in src/terok_agent/config_stack.py
def deep_merge(base: dict, override: dict) -> dict:
    """Recursively merge *override* into *base*, returning a **new** dict.

    Rules
    -----
    * Dicts are merged recursively by default.
    * A ``None`` value in *override* **deletes** the corresponding key.
    * A bare ``"_inherit"`` string keeps the base value unchanged
      (equivalent to omitting the key, but explicit).
    * Lists in *override* replace the base list wholesale **unless** the
      list contains the sentinel string ``"_inherit"``, in which case the
      sentinel is replaced by the base list elements (splice).
    * A dict in *override* that contains ``_inherit: true`` keeps all
      parent keys and overlays the rest (the ``_inherit`` key itself is
      stripped from the result).
    """
    merged: dict = {}

    all_keys = set(base) | set(override)
    for key in all_keys:
        if key in override:
            ov = override[key]
            # None → delete
            if ov is None:
                continue
            # Bare _inherit string → keep base value (explicit no-op)
            if ov == _INHERIT:
                if key in base:
                    merged[key] = base[key]
                continue
            bv = base.get(key)
            if isinstance(ov, dict) and isinstance(bv, dict):
                merged[key] = _merge_dicts(bv, ov)
            elif isinstance(ov, list) and isinstance(bv, list):
                merged[key] = _merge_lists(bv, ov)
            else:
                merged[key] = ov
        else:
            # key only in base
            merged[key] = base[key]
    return merged

load_yaml_scope(level, path)

Load a YAML file into a ConfigScope. Returns empty data if missing.

Source code in src/terok_agent/config_stack.py
def load_yaml_scope(level: str, path: Path) -> ConfigScope:
    """Load a YAML file into a ConfigScope.  Returns empty data if missing."""
    if path.is_file():
        data = _yaml_load(path.read_text(encoding="utf-8")) or {}
    else:
        data = {}
    return ConfigScope(level=level, source=path, data=data)

load_json_scope(level, path)

Load a JSON file into a ConfigScope. Returns empty data if missing.

Source code in src/terok_agent/config_stack.py
def load_json_scope(level: str, path: Path) -> ConfigScope:
    """Load a JSON file into a ConfigScope.  Returns empty data if missing."""
    if path.is_file():
        data = json.loads(path.read_text(encoding="utf-8")) or {}
    else:
        data = {}
    return ConfigScope(level=level, source=path, data=data)