A tiny, opinionated configuration loader and CLI framework built around Pydantic models.
- YAML Configuration Loading — load and merge multiple files with left-to-right precedence
- Smart Imports —
!importwith deep-merging, paths resolved relative to the importing file - Runtime Overrides — JSONPath-like syntax via
--override - Pydantic Validation — automatic validation and type coercion
- Schema Generation —
--print-schemarenders a formatted JSON schema;--print-configdumps the merged result - Rich CLI Interface — styled help, errors, and schema output via Rich
- Subcommands — multi-command CLIs via
Nested; each command owns its own config model - Type Safety — pyright strict, modern Python type hints
pip install git+https://github.com/cusp-ai-oss/nanoargs.gitRequires Python 3.10+. Dependencies: lark, pydantic, pyyaml, rich, rich-argparse.
from pydantic import BaseModel
from nanoargs import NanoArgs
class Config(BaseModel):
mode: str = "train"
batch_size: int = 32
learning_rate: float = 0.001
config = NanoArgs(Config).parse()python app.py config.yaml # load a YAML file
python app.py base.yaml exp.yaml # merge multiple files
python app.py config.yaml --override '$.batch_size=128' # JSONPath override
echo 'batch_size: 128' | python app.py - # read from stdin
python app.py --print-schema # inspect the schema
python app.py config.yaml --print-config # inspect merged resultUse Nested to define multi-command CLIs. Fields typed as T | None = None become subcommands:
from nanoargs import NanoArgs, Nested
class TrainConfig(BaseModel):
lr: float = 0.001
epochs: int = 10
class EvalConfig(BaseModel):
checkpoint: str
class CLI(Nested):
verbose: bool = False # pre-command parent field
train: TrainConfig | None = None
evaluate: EvalConfig | None = None
result = NanoArgs(CLI).parse()python app.py train config.yaml --override '$.lr=0.01'
python app.py --override '$.verbose=true' evaluate config.yaml
python app.py --help # lists available commands
python app.py train --print-schema # schema for a specific commandNested composes naturally — a field whose type is itself Nested creates a sub-group (app data preprocess config.yaml).
See the Subcommands guide for full details.
- Getting Started — full walkthrough
- Configuration Guide — import system, merge rules, override syntax, CLI flags
- Subcommands —
Nestedpatterns, parent fields, multi-level nesting - Path Syntax — JSONPath-like grammar reference
- API Reference
- Changelog
- YAML booleans in config files:
yes,no,on,offare booleans in YAML. Quote them for strings:"yes". This does not affect--overridevalues. - List merging: config file merging replaces lists entirely — it does not append.
- Hyphenated keys:
--overridepaths require bracket syntax:--override '$["learning-rate"]=0.001'. - Negative array indices:
$[-1]works Python-style (last element). Negative slice bounds are also supported:$[-3:],$[:-1]. - Bracket notation quotes: both
$["key"]and$['key']are supported. --print-configwithout config files: prints Pydantic-validated defaults and exits.- Unknown fields: overriding a path that doesn't match any model field is silently ignored by Pydantic.
- Auto-vivification:
$.a.b.c=1creates intermediate dicts for missing keys orNonevalues. @fileresolution: from the CLI,@fileresolves relative to the working directory; inside YAML (!override), relative to the YAML file. Use@@to escape a literal@.
Apache 2.0