API Reference¶
Config¶
Nested configuration container. Nested dicts are automatically wrapped into child Config instances.
Access¶
| Operation | Example |
|---|---|
| Attribute read | cfg.db.host |
| Dotted-path read | cfg["db.host"] |
| Attribute write | cfg.db.host = "x" |
| Dotted-path write | cfg["db.host"] = "x" |
| Containment | "db.host" in cfg |
| Delete | del cfg.db.host |
Methods¶
get(path, default=None)— safe dotted-path readkeys(),values(),items()— top-level iterationto_dict()— recursive plain-dict exportto_json(**kwargs)— JSON string; passes kwargs tojson.dumpsto_toml()— TOML stringto_yaml(**kwargs)— YAML string; passes kwargs toyaml.dumpsave_json(path, **kwargs)/save_toml(path)/save_yaml(path, **kwargs)— write the config to a filefreeze()— recursively freeze; raisesFrozenConfigErroron writeunfreeze()— recursively unfreezesnapshot()— push current state onto the snapshot stackrollback()— pop and restore the last snapshot (raisesConfigErrorif the stack is empty)on_change(callback)— registercallback(path, new_value, old_value)meta(path="")— return{"source": str, "history": tuple}for a dotted path, orNoneif the path has no recorded metadatametadata()— return the full metadata dict
Properties¶
frozen—Trueif the config is currently frozen
Class-level loaders¶
Config.load_json(path) # -> Config
Config.load_toml(path) # -> Config
Config.load_yaml(path) # -> Config
Convenience shortcuts that read a single file. Source tracking is not
recorded — for provenance, load through ConfigLoader with the matching
*FileSource instead.
Sources¶
All sources inherit from Source and expose a single method:
load() -> dict[str, Any]
Every source accepts an optional name. When omitted (None), the source's class name is recorded in metadata; the name is what appears in Config.meta() history.
DictSource¶
Wraps an in-memory dict.
JsonFileSource¶
TomlFileSource¶
YamlFileSource¶
Loads a YAML file (requires pyyaml). An empty file yields {}.
EnvSource¶
EnvSource(
prefix: str = "",
separator: str = "_",
environ: dict | None = None,
*,
coerce: bool = True,
name: str | None = None,
)
Reads environment variables and maps them to nested keys by splitting on separator (default _). prefix is stripped and not included in the output key.
CliSource¶
Parses --key=value and --key value arguments. Dotted keys map to nested dicts.
Merge¶
merge()¶
Returns a new dict. Neither input is mutated.
MergeStrategy¶
ConfigLoader¶
load() -> Config— merge all sources in order, attach metadata
ProfileLoader¶
ProfileLoader(base_sources: list[Source], profiles: dict[str, Source], strategy: MergeStrategy = DEEP_MERGE)
load(profile: str | None = None) -> Config— load base sources and optionally apply a named profile overlay
Validation¶
validate()¶
validate(
data: dict,
schema: type,
prefix: str = "",
*,
allow_extra: bool = True,
apply_defaults: bool = False,
) -> dict
Raises ValidationError on type mismatch, missing required fields, or constraint failure. Returns the input dict unchanged unless apply_defaults=True, in which case a new dict with defaults filled in is returned. prefix prefixes reported error paths and is mainly used for nested validation.
Constraints¶
Range(min_val: int | float, max_val: int | float) # inclusive [min_val, max_val]
Length(min_len: int = 0, max_len: int | None = None)
Pattern(pattern: str) # re.search against the value
OneOf(*values)
Attach via __constraints__ = {"field": [constraint, ...]} on the schema class.
Build¶
typing.Annotated metadata marker. When validate() encounters
field: Annotated[T, Build(reg)], the raw value is passed through
reg.build(...) before the field's type is checked. Works on default
values too.
Registry¶
String-tag → factory container. Generic: Registry[T]("name").
Registration¶
reg.register("silu", nn.SiLU) # direct
@reg("leaky_relu") # decorator
class MyLeakyReLU(nn.Module): ...
Keys are lowercased. Duplicate registration raises ValueError.
Access¶
reg.build(spec) -> T | None— resolve into an instance. Acceptsstr(short form),dictwith atypekey + kwargs (long form),None(pass-through), or an existing instance (idempotent).reg.get(name) -> type[T] | callable | None— return the registered factory/class without instantiating.Nonepass-through. For APIs that taketype[T]and construct later.reg.keys() -> list[str]— sorted list of registered keys."key" in reg— containment check (case-insensitive).
See the Registry guide for the full tutorial.
Concurrency¶
ThreadSafeConfig¶
Wraps all reads and writes in the provided lock (or a new RLock). Exposes the same interface as Config.
FileLock¶
Context manager. Acquires an exclusive lock on entry (fcntl.LOCK_EX on POSIX, msvcrt.locking on Windows) and releases it on exit.
acquire()/release()for manual management
interpolate()¶
Resolves ${path.to.key} and ${env:VAR} placeholders. Raises CircularReferenceError on circular references.
Paths¶
project_config_dir()¶
Returns ~/.molcrafts/<name>/config/, creating it (and any missing parents)
if absent, so downstream tools share a stable user-level configuration
directory. name must be a single path segment — not empty, ., .., or
containing /, \, or os.sep (otherwise ValueError is raised and no
directory is created). If the MOLCRAFTS_HOME environment variable is set to
a non-empty value it overrides the ~/.molcrafts base; empty or
whitespace-only values fall back to the default. Pass environ= to inject a
mapping instead of reading os.environ.
Errors¶
All errors inherit from ConfigError.
| Exception | Raised when |
|---|---|
ConfigError |
Base class |
FrozenConfigError |
Writing to a frozen Config |
ValidationError |
Schema validation fails |
CircularReferenceError |
Interpolation detects a cycle |