tlcconfig.store¶
Per-source ledger of raw configuration values.
The store records each option’s value alongside the source that wrote it (file / environment / CLI / programmatic API), so callers can answer “which tier won?”. It performs no validation, transformation, or business logic — those live one layer up.
.. rubric:: Example
from tlcconfig.store import ConfigStore
store = ConfigStore()
port = store.get("service.port")
raw = store.get_raw("service.port")
print(f"{raw.value} from {raw.source.name}")
Module Contents¶
Classes¶
Class |
Description |
|---|---|
Lightweight configuration store. |
|
A single sub-element of a compound option, with its own provenance. |
|
A raw loaded value with source tracking. |
Data¶
Data |
Description |
|---|---|
API¶
- class ConfigStore( )¶
Lightweight configuration store.
This store is intentionally “dumb” — it reads values and tracks sources, but performs no validation or transformation. This keeps it fast to import and free of side effects.
The store follows a precedence order (highest to lowest):
API (programmatic setting via set())
Command line arguments
Environment variables
Config file
Secondary config files (project-level configs)
Defaults (from registered options)
- Variables:
config_files – List of config files that were loaded.
Initialize the configuration store.
- Parameters:
config_file – Path to config file. If None, auto-discovers. If empty string “”, skips config file loading.
load_defaults – Whether to load default values from registered options.
load_env – Whether to load values from environment variables.
- add_item( ) None¶
Add a single sub-element to a compound option without replacing the rest of the slice.
Unlike :meth:
set_value(which replaces the entire(source, source_info)slice with the new value),add_itemappends oneProvenancedItemleaving any other items already present at the same source intact. Used by the facade verbsappend/updateand by callers that want surgical, single-element writes.- Parameters:
key – Option key.
sub_key – Dict sub-key to set, or
Nonefor list-compound append.value – Value to set / append.
source – ConfigSource for this write.
source_info – Additional source context.
- Raises:
ValueError – If the option is not registered or is not a compound.
ValueError – For dict-compound options when
sub_keyisNone.
- bootstrap_from_root_config() None¶
Load
<ROOT_URL>/config.3lc.yamlif it exists.Resolves
ROOT_URLfrom this store, joins it with the default config-file name, and loads the file atPROJECT_ROOT_CONFIG_FILEprecedence if present. No-op whenROOT_URLis unset or no file exists at the resolved path.Lives on the store (not on
Configuration) so callers that only needtlcconfig— notablytlccli.main’s root callback — can invoke it without triggeringimport tlc/init_global_objects.
- claim_pending(
- key: str,
Move any pending entries for
keyinto the live store.Idempotent. Called by
OptionRegistry.registerwhen an option is registered after a config source has already been loaded. Routes each pending entry through_set_rawso compound options get their per-item splitting applied retroactively (the option wasn’t registered when the value was first stashed, so the splitter couldn’t see it then — now it can).
- claim_pending_env( ) None¶
Drain stashed
TLC_*env vars matchingoption’s envvar bindings.Mirrors the eager-pass logic in
_load_environment: canonical wins over deprecated aliases, regex envvars match every stashed name once. Deprecation warnings fire here (claim time) rather than at load time because the option wasn’t registered when the env was scanned.Idempotent. Called by
OptionRegistry.register.
- property config_files: Mapping[str, ConfigSource]¶
Read-only mapping of loaded config file paths to the tier they were loaded at, in load order.
inmembership, iteration (yields paths), and.get(path)all work as expected. For the first user-tier file (the writable target for3lc config project-root) use :attr:user_config_path. For tier-filtered lookups use :meth:files_at.
- files_at(
- source: ConfigSource,
Return the loaded config files at the given tier, in load order.
- get(
- key: str,
Get the highest-precedence raw value for a key.
- Parameters:
key – Option key (e.g., “service.port”).
- Returns:
The raw value, or None if not set.
- get_all_raw(
- key: str,
Get all raw values for a key (useful for compound types).
For non-compound keys: returns the list of raw entries verbatim. For compound keys: synthesizes one
RawValueper(source, source_info)group by reassembling the per-itemProvenancedItementries — back-compat shim for callers that used to receive whole-tier writes here. Use- Meth:
iter_provenancedif you actually want per-item provenance.- Parameters:
key – Option key.
- Returns:
List of all RawValues for the key.
- get_merged(
- key: str,
Get the merged value for compound types, or highest-precedence for simple types.
For compound types (dict/list), merges all per-item entries in precedence order (lowest first so higher precedence overwrites on dict-key collisions; latest-wins on list-item structural equality). The wrapper’s
sourceis set to- Class:
ConfigSource.MIXEDwhen items span tiers, otherwise the single tier shared by all items. For simple types, returns the highest-precedence value (same as get_raw).- Parameters:
key – Option key.
- Returns:
RawValue with merged value and provenance summary, or None if not set.
- get_provenance(
- key: str,
Return per-element provenance, polymorphic by the option’s compound type.
Scalar option (or unregistered key): single
- class:
ProvenancedItemwith the highest-precedence value, orNoneif unset.
List-compound option:
list[ProvenancedItem]in merged order, deduped by structural identity. The first tier (lowest precedence) that contributed each item is the surviving provenance — matches theget_mergeddedup rule. Empty list if unset.Dict-compound option:
dict[K, ProvenancedItem]keyed by sub-key. Per-key, the highest-tier writer wins. Empty dict if unset.
Pure query — does not mutate the store, never warns. Use the returned container with native Python idioms; there are no
index=orsub_key=kwargs (you index/iterate the container yourself).- Parameters:
key – Option key.
- Returns:
Polymorphic provenance view; see above.
- get_raw(
- key: str,
Get raw value with source info.
For compound options, delegates to :meth:
get_mergedso callers that haven’t been ported to per-item provenance still see a singleRawValuewrapper. The wrapper’ssourcewill be- Class:
ConfigSource.MIXEDif items span tiers.- Parameters:
key – Option key.
- Returns:
RawValue with value and source info, or None if not set.
- get_source(
- key: str,
Get the source of a value.
- Parameters:
key – Option key.
- Returns:
The ConfigSource, or None if not set.
- classmethod instance() ConfigStore¶
Get the global ConfigStore instance.
Creates a new instance if none exists.
- Returns:
The global ConfigStore instance.
- load_cli( ) None¶
Load values from parsed CLI arguments.
- Parameters:
args – Dictionary of CLI argument values (from argparse or click).
- load_config(
- path: str,
- source: ConfigSource = ConfigSource.USER_CONFIG_FILE,
Load a YAML config file at the given source tier.
Source-explicit public loader. Reloading the same
(path, source)replaces any previously loaded entries from that path at that tier so callers can re-invoke this method when the file changes.- Parameters:
path – Path to the YAML file.
source – The :class:
ConfigSourcetier to load at. Defaults toUSER_CONFIG_FILE.
- load_data_config(
- path: str,
Load a data-bundled (secondary) config file. Wrapper for
- Meth:
load_configatDATA_CONFIG_FILEtier.
Lower precedence than project-root, user-global, environment, and command line. Used for config files discovered alongside project data — e.g. shipped on a read-only mount or scanned via the indexing table.
- load_project_root_config(
- path: str,
Load a project-root
config.3lc.yaml. Wrapper for- Meth:
load_configatPROJECT_ROOT_CONFIG_FILEtier.
Beats data-bundled, loses to user-global so the user’s writable global config still wins.
- pending_count() int¶
Return the number of unclaimed pending entries.
Sum of YAML keys parked in
_pending_unknownandTLC_*env vars parked in_pending_env— i.e. values the user supplied that no Option has claimed yet. Pure query; does not warn. Callers can build optional strict modes, status panes, or post-bootstrap diagnostics on top of this.
- pending_unknown_keys() list[str]¶
Return the list of keys still waiting for an Option to claim them.
- remove_item(
- key: str,
- sub_key_or_value: Any,
- source: ConfigSource,
- source_info: str | None = None,
Remove a single sub-element from a compound option at the given tier.
For dict-compound options,
sub_key_or_valueis the sub-key to remove. For list-compound options, it’s the value to match by structural identity (the samejson.dumps(sort_keys=True)ruleget_mergeduses for dedup). Only items at the specified(source, source_info)tier are affected — lower-tier items resurface in the merged view.- Parameters:
key – Option key.
sub_key_or_value – For dict, the sub-key to remove; for list, the value to match.
source – ConfigSource of the entry to remove.
source_info – Source-info of the entry to remove.
- Returns:
True if at least one item was removed, False otherwise.
- Raises:
ValueError – If the option is not registered or is not a compound.
- set( ) None¶
Programmatically set a value (highest precedence).
- Parameters:
key – Option key.
value – Value to set.
- classmethod set_instance(
- store: ConfigStore,
Set the global ConfigStore instance.
Used by CLI to set up the store before subcommands run.
- Parameters:
store – The store instance to use globally.
- set_value(
- key: str,
- value: Any,
- source: ConfigSource,
- source_info: str | None = None,
Set a value with an explicit source.
Any existing raw entry with the same
(source, source_info)tuple is replaced so repeated calls stay idempotent and the internal raw value list does not grow unboundedly. For compound options the sweep operates on the per-item store (_compound).- Parameters:
key – Option key.
value – Value to set.
source – The ConfigSource indicating where this value came from.
source_info – Additional context about the source (e.g., file path).
- stash_unknown(
- key: str,
- value: Any,
- source: ConfigSource,
- source_info: str | None = None,
Record a value for a key that no Option has claimed yet.
Used by loaders (and by callers contributing unknown values from higher-precedence tiers) so that a later
OptionRegistry.registercan replay the value at its original source. Seeclaim_pending.
- to_default_yaml(
- include_docs: bool = True,
Generate YAML with only default values.
- Parameters:
include_docs – Whether to include documentation comments.
- Returns:
YAML-formatted configuration with defaults.
- to_yaml( ) str¶
Serialize current configuration to YAML format.
- Parameters:
include_docs – Whether to include documentation comments.
detail – Whether to include source info for non-default values.
- Returns:
YAML-formatted configuration string.
- property user_config_path: str | None¶
First file loaded at
USER_CONFIG_FILEtier, orNone.Authoritative answer to “where do I write user-level config?” — used by
tlccli config project-root. Returning the first file in- Attr:
config_filesis wrong: it can be a DATA-tier file the indexer picked up before the user-tier load.
- validate() None¶
Validate that required options are set.
- Raises:
ValueError – If any required options are missing or empty.
- warn_unclaimed_pending() None¶
Emit a WARNING for each newly-unclaimed pending key/env var.
Intended to be called at the end of bootstrap (e.g. in
init_global_objects) so plugin keys that never got claimed — typos, removed-but-still-configured options, plugins not yet imported — surface to the user instead of silently rotting.The pending stash is not cleared: a plugin imported later in the same process can still claim its values via the late-bind path. Already-warned entries are tracked so repeated checkpoint calls (e.g. periodic re-init in long-running services) stay quiet unless new unclaimed entries appear. Use
pending_countto observe the queue size programmatically.
- write_to_yaml_file( ) bool¶
Write configuration to a YAML file.
- Parameters:
path – Path to write to.
force – Whether to overwrite existing file.
include_docs – Whether to include documentation comments.
detail – Whether to include source info.
- Returns:
True if file was written, False if file exists and force=False.
- class ProvenancedItem¶
A single sub-element of a compound option, with its own provenance.
Compound options (lists, dicts) can mix items from different sources — e.g. some scan-urls from YAML, others from env vars. Each item is stored independently so callers can ask “where did this URL come from?” via :meth:
ConfigStore.get_provenance(which returnslist[ProvenancedItem]for list-compounds,dict[K, ProvenancedItem]for dict-compounds, or a single instance for scalars). Frozen so instances are hashable and safe to dedupe.- Variables:
value – The single sub-element (a list element, or a one-key dict slice
{k: v}for dict-compounds).source – Where this specific element came from.
source_info – Additional context (filename, env var name).
- source: ConfigSource = None¶
- class RawValue¶
A raw loaded value with source tracking.
- Variables:
value – The raw value as loaded (no transformation applied).
source – Where the value came from (DEFAULT, USER_CONFIG_FILE, ENVIRONMENT, etc.).
source_info – Additional context (filename, env var name, CLI flag).
- source: ConfigSource = None¶
- logger = getLogger(...)¶