Skip to main content

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 factory
  • description — shown in explain() output and in YAML templates produced by eject()
  • env — explicit environment-variable name used by env_overlay()
  • secret — when True, explain() masks the value; useful for passwords and API keys
  • validator — a callable (value) -> bool | str; a falsy return or raised ValueError becomes a ValidationError

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 defaults
  • pyproject.toml, [tool.kaava] section — project-level, if the section exists
  • kaava.toml, kaava.yaml, or kaava.json in 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kaava-2026.6.8.tar.gz (33.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kaava-2026.6.8-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

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

Hashes for kaava-2026.6.8.tar.gz
Algorithm Hash digest
SHA256 3d7fca46aae36aa88fecaee98da3095a84ff807ee6252a10db6a64df622959c3
MD5 e68d32799f49b7936c605724acd674e6
BLAKE2b-256 8472b0158734dc90cdbc720c87261c7913b087b27429044cab592fd62fdaaec6

See more details on using hashes here.

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

Hashes for kaava-2026.6.8-py3-none-any.whl
Algorithm Hash digest
SHA256 8f8cc4ef9e869495a9237f7c11f2d3c6323605e6148915b62d7d2a1557bddf39
MD5 68d6666a46e0b12d0b8ec42f734f327b
BLAKE2b-256 c179ff475c77a7b1b3e00c31a6566248f07029f999320d83e3ab004d90d829e4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page