config_stack
config_stack
¶
Resolves layered configuration by deep-merging ordered scopes.
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
_inheritsupport.
__all__ = ['ConfigScope', 'ConfigStack', 'deep_merge', 'load_json_scope', 'load_yaml_scope']
module-attribute
¶
ConfigScope(level, source, data)
dataclass
¶
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_util/config_stack.py
scopes
property
¶
Read-only access to the scope list (for diagnostics).
push(scope)
¶
resolve()
¶
Deep-merge all scopes in order and return the result.
resolve_section(key)
¶
Resolve only a single top-level section across all scopes.
Respects the same semantics as
resolve — in
particular, None values trigger deletion via
deep_merge.
Returns {} when the highest-priority scope has a non-dict
value for key (e.g. services: tcp instead of
services: {mode: tcp}). Callers can call
resolve and
inspect the raw shape if they need to distinguish "missing"
from "wrong-shape", but resolve_section is contract-typed
as a mapping accessor and so coerces non-mappings to empty.
Source code in src/terok_util/config_stack.py
load_yaml_scope(level, path)
¶
Load a YAML file into a ConfigScope.
Returns an empty-data scope when the file is missing or empty.
Raises¶
ValueError
If the parsed YAML is not a mapping.
ConfigScope data
must be a dict because
ConfigStack.resolve
and deep_merge operate
on mappings.
Source code in src/terok_util/config_stack.py
load_json_scope(level, path)
¶
Load a JSON file into a ConfigScope.
Returns an empty-data scope when the file is missing or empty.
Raises¶
ValueError
If the parsed JSON is not a mapping.
ConfigScope data
must be a dict because
ConfigStack.resolve
and deep_merge operate
on mappings.
Source code in src/terok_util/config_stack.py
deep_merge(base, override)
¶
Recursively merge override into base, returning a new dict.
Rules¶
- Dicts are merged recursively by default.
- A
Nonevalue 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: truekeeps all parent keys and overlays the rest (the_inheritkey itself is stripped from the result).