slimconfig
YAML configs merged onto typed dataclass schemas — a lightweight Hydra stand-in in ~300 lines, built on OmegaConf.
Two rules, enforced at load time:
- Every field is required. A schema's leaves all default to
MISSING, so a config has to set each one explicitly — a nullable field that is "off" is still written out asnull, an empty collection as[]. Nothing is silently inherited. - Unknown keys are rejected. A typo in a YAML key is an error, not a value that goes nowhere.
Plus what a research/experiment runner actually needs: Hydra-style defaults: composition, a
mode dispatcher, and a run folder that snapshots the exact config it ran with.
Install
pip install slimconfig
Load a config
# train.py
import sys
from dataclasses import dataclass, field
from omegaconf import MISSING
from slimconfig import load_config
@dataclass
class Optim:
lr: float = MISSING
warmup_steps: int = MISSING
@dataclass
class TrainConfig:
run_dir: str = MISSING
model: str = MISSING
optim: Optim = field(default_factory=Optim)
resume_from: str | None = MISSING # "off" must still be spelled `null`
cfg = load_config(TrainConfig, sys.argv[1:]) # -> a real TrainConfig instance
print(cfg.optim.lr)
# configs/train.yaml
run_dir: runs/${now:%Y%m%d-%H%M%S}
model: llama-3-8b
optim:
lr: 2.0e-4
warmup_steps: 100
resume_from: null
python train.py configs/train.yaml # a file
python train.py configs/train.yaml optim.lr=1e-4 # ...plus dotted overrides, later wins
Leave warmup_steps out and the load fails with
TrainConfig is missing required field(s): optim.warmup_steps — before anything runs.
Share configs with defaults:
Any YAML may carry a top-level defaults: list of paths. Listed files merge first, in order, and
the current file wins on top; composition is recursive, and cycles are caught.
# configs/train_7b.yaml
defaults: [configs/train.yaml, configs/optim/cosine.yaml]
model: llama-3-7b
Paths resolve relative to the current working directory (the project root scripts are launched from), so one path convention holds wherever the including file lives. Absolute paths work too.
Interpolation resolvers
On top of OmegaConf's own ${a.b} interpolation, importing slimconfig registers:
| Resolver | Meaning |
|---|---|
${now:%Y%m%d-%H%M%S} |
the load time, strftime-formatted — one consistent stamp per process |
${from_yaml:configs/data.yaml,dataset.name} |
one value read out of another config, so a config can track a value another file owns without duplicating it |
Dispatch on mode
For a single entry point that fans out to several jobs, dispatch reads mode, opens and
snapshots run_dir, then calls the matching handler:
# run.py
import sys
from slimconfig import dispatch
MODES = {
"train": (TrainConfig, train), # load TrainConfig strictly, call train(cfg)
"eval": (EvalConfig, evaluate),
"sweep": run_sweep, # bare handler: gets the raw specs, loads its own schema
}
raise SystemExit(dispatch(MODES, sys.argv[1:]))
mode and run_dir are ordinary config keys, so a schema loaded this way declares them itself
(unknown keys are rejected).
Run folders
start_run(run_dir, config) (called for you by dispatch) creates the folder and writes:
config.yaml— the fully-resolved config, re-runnable as-is:python run.py <run_dir>/config.yamlrun_meta.json— argv, cwd, git commit + dirty flag, start time, host
Everything a run produces goes in that same folder, so a result is never separated from the config that made it. The snapshot is best-effort — provenance never aborts a run.
API
load_config(schema, specs) |
merge specs onto a dataclass schema → a populated instance |
merge_specs(specs) |
merge specs into one unvalidated DictConfig |
peek(specs, key) |
read one top-level key before choosing a schema |
dispatch(modes, specs) |
mode → handler, with the run folder opened and snapshotted |
start_run(run_dir, config) |
create the run folder, write config.yaml + run_meta.json |
load_mapping_yaml(path) |
one YAML → DictConfig, with defaults: composed |
load_yaml(path) |
one YAML → dict, plain PyYAML, no composition |
A spec is a YAML file path, a dotted.key=value string, or a ready-made mapping/DictConfig —
so a caller can merge values it computed at runtime under the same "later wins" rule.
Development
pip install -e ".[dev]"
pytest
ruff check .
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file slimconfig-0.1.0.tar.gz.
File metadata
- Download URL: slimconfig-0.1.0.tar.gz
- Upload date:
- Size: 14.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
215250bcbaa03a263ec471d7a6755af04bb515e2d53b2f054458946dd2670532
|
|
| MD5 |
a755bdf89fb93ba3c2f59be70715e222
|
|
| BLAKE2b-256 |
a3414b212399395bf194095268fb3f3d797221a2cce6e71c9adefaf0f858c966
|
File details
Details for the file slimconfig-0.1.0-py3-none-any.whl.
File metadata
- Download URL: slimconfig-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd896b58434ce36b2add000a5b4a1d2865f7223ab2f5ac9d25fce274b5093465
|
|
| MD5 |
582f9e5cec9b6fc5c53b8ec07c801da2
|
|
| BLAKE2b-256 |
7282de67836e34f844f001728819b1bfcb1659173fa29326663f0e6bf58dbb4c
|