Formula / schema / pattern (Finnish: kaava) - load typed Python configuration from YAML, TOML, or JSON into dataclasses, with validation, source tracing, environment-variable overlays, and a diagnostic CLI.
Project description
kaava
Load typed Python configuration from YAML, TOML, or JSON into dataclasses, with validation, source tracing, environment-variable overlays, and a diagnostic CLI.
Requires Python 3.11 or later. YAML support uses PyYAML; TOML and JSON use the standard library.
Install
pip install kaava
Quick start
Define your configuration shape as a plain dataclass and call load().
import dataclasses
from kaava import conf_field, load
@dataclasses.dataclass
class DBConfig:
host: str = conf_field(description='database host')
port: int = conf_field(default=5432)
@dataclasses.dataclass
class AppConfig:
name: str = conf_field(description='application name')
db: DBConfig = conf_field(description='database connection')
debug: bool = conf_field(default=False)
cfg = load(AppConfig, 'config.yaml')
print(cfg.name, cfg.db.host, cfg.db.port)
config.yaml:
name: myapp
db:
host: localhost
Fields with no default or default_factory are required.
Nested dataclasses map to YAML mappings and are built recursively.
load() raises on the first error encountered; use validate() or load_valid() to collect all errors.
For a quick feature-by-feature tour, see quickstart/README.md.
For a step-by-step tutorial that builds a real command-line tool from scratch, see tutorial/README.md.
Field metadata
conf_field() extends dataclasses.field() with configuration-specific metadata.
default/default_factory— the field's default value or factorydescription— shown inexplain()output and in YAML templates produced byeject()env— explicit environment-variable name used byenv_overlay()secret— whenTrue,explain()masks the value; useful for passwords and API keysvalidator— a callable(value) -> bool | str; a falsy return or raisedValueErrorbecomes aValidationError
Sources
load() accepts one or more file paths (str or pathlib.Path) or https:// URLs.
The format is inferred from the file extension (.yaml / .yml, .toml, .json); YAML is the default for unrecognised extensions.
cfg = load(AppConfig, 'base.yaml', 'prod.yaml')
Sources are applied in order — later sources deep-merge over earlier ones. A nested key in a later source only replaces its own subtree, leaving sibling keys from earlier sources intact.
Auto-discovery
When called with no sources, load() searches for configuration in standard locations, in this order:
~/.config/kaava.toml— user-level defaultspyproject.toml,[tool.kaava]section — project-level, if the section existskaava.toml,kaava.yaml, orkaava.jsonin the working directory
Later entries override earlier ones via the same deep-merge. Passing at least one explicit source skips discovery entirely.
Environment-variable overlays
Overlays are applied on top of all sources, last-wins.
from kaava import env_overlay, load
overlay = env_overlay(AppConfig, prefix='APP_')
cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
With prefix='APP_', the field db.host maps to APP_DB_HOST.
An explicit env= annotation on a field always takes precedence over the derived name.
env_overlay() reads from os.environ by default; pass environ= to supply a custom mapping.
Dotenv files
from kaava import dotenv_environ, env_overlay, load
environ = dotenv_environ('.env')
overlay = env_overlay(AppConfig, environ=environ, prefix='APP_')
cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
dotenv_environ() parses KEY=VALUE pairs.
Blank lines and # comments are skipped.
An export KEY=VALUE prefix and surrounding quotes are stripped.
CLI arguments
from kaava import cli_from_namespace, load
overlay = cli_from_namespace(vars(args))
cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
cli_from_namespace() accepts an argparse.Namespace or a plain dict.
Dotted keys (db.host) are unflattened into the nested config structure automatically.
Collecting all errors
validate() returns every error as a list without raising, which is useful for CLI tools that want to report all problems at once.
from kaava import validate
errors = validate(AppConfig, 'config.yaml')
for e in errors:
print(e)
load_valid() does the same collection but raises ConfigErrors at the end if any errors were found.
Source tracing and explain
load_traced() returns the config kaava together with a SourceMap recording where each field value came from.
from kaava import explain, load_traced
cfg, source_map = load_traced(AppConfig, 'base.yaml', 'prod.yaml')
print(explain(cfg, source_map))
explain() renders a table of field paths, current values, sources, and descriptions.
Pass show='non-default' to show only fields whose value came from a source, not a dataclass default.
Diagnostics
doctor() probes every source, captures all load and validation errors without raising,
and reports which environment variables declared in the dataclass are set or unset.
from kaava import doctor
report = doctor(AppConfig, 'config.yaml', prefix='APP_')
print(report)
print('ok:', report.ok)
The returned DoctorReport has fields sources_ok, sources_failed, errors,
set_env, unset_env, kaava, and source_map.
report.ok is True only when no sources failed and no errors were found.
Ejecting a template
eject() prints a YAML scaffold derived from the dataclass schema.
Required fields are annotated with # required; defaults, descriptions, and env-var names appear as inline comments.
from kaava import eject
print(eject(AppConfig))
Companion CLI
The kaava command exposes the four diagnostic functions as subcommands.
The target dataclass is identified by a dotted import path of the form module.path:ClassName.
kaava doctor myapp.config:AppConfig config.yaml
kaava explain myapp.config:AppConfig config.yaml
kaava validate myapp.config:AppConfig config.yaml
kaava eject myapp.config:AppConfig
See man kaava for the full reference, including --env-prefix, --dotenv, and exit codes.
SBOM
Runtime dependency information is published in docs/sbom/ in SPDX 3.0 (JSON-LD) and CycloneDX 1.6 (JSON) formats.
See docs/sbom/README.md for the component inventory and validation guide.
Project details
Release history Release notifications | RSS feed
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 kaava-2026.6.8.tar.gz.
File metadata
- Download URL: kaava-2026.6.8.tar.gz
- Upload date:
- Size: 33.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d7fca46aae36aa88fecaee98da3095a84ff807ee6252a10db6a64df622959c3
|
|
| MD5 |
e68d32799f49b7936c605724acd674e6
|
|
| BLAKE2b-256 |
8472b0158734dc90cdbc720c87261c7913b087b27429044cab592fd62fdaaec6
|
File details
Details for the file kaava-2026.6.8-py3-none-any.whl.
File metadata
- Download URL: kaava-2026.6.8-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f8cc4ef9e869495a9237f7c11f2d3c6323605e6148915b62d7d2a1557bddf39
|
|
| MD5 |
68d6666a46e0b12d0b8ec42f734f327b
|
|
| BLAKE2b-256 |
c179ff475c77a7b1b3e00c31a6566248f07029f999320d83e3ab004d90d829e4
|