kaava 2026.6.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. kaava-2026.6.8/COUNTRY-OF-ORIGIN +1 -0
  2. kaava-2026.6.8/EXPORT-CONTROL-CLASSIFICATION-NUMBER +1 -0
  3. kaava-2026.6.8/LICENSE +21 -0
  4. kaava-2026.6.8/MANIFEST.in +5 -0
  5. kaava-2026.6.8/PKG-INFO +226 -0
  6. kaava-2026.6.8/README.md +204 -0
  7. kaava-2026.6.8/docs/kaava.1 +222 -0
  8. kaava-2026.6.8/kaava/__init__.py +93 -0
  9. kaava-2026.6.8/kaava/__main__.py +5 -0
  10. kaava-2026.6.8/kaava/_builder.py +201 -0
  11. kaava-2026.6.8/kaava/_cli.py +152 -0
  12. kaava-2026.6.8/kaava/_discover.py +47 -0
  13. kaava-2026.6.8/kaava/_doctor.py +118 -0
  14. kaava-2026.6.8/kaava/_eject.py +73 -0
  15. kaava-2026.6.8/kaava/_errors.py +25 -0
  16. kaava-2026.6.8/kaava/_explain.py +53 -0
  17. kaava-2026.6.8/kaava/_fields.py +19 -0
  18. kaava-2026.6.8/kaava/_merge.py +34 -0
  19. kaava-2026.6.8/kaava/_overlays.py +83 -0
  20. kaava-2026.6.8/kaava/_sources.py +60 -0
  21. kaava-2026.6.8/kaava/py.typed +0 -0
  22. kaava-2026.6.8/kaava.egg-info/PKG-INFO +226 -0
  23. kaava-2026.6.8/kaava.egg-info/SOURCES.txt +45 -0
  24. kaava-2026.6.8/kaava.egg-info/dependency_links.txt +1 -0
  25. kaava-2026.6.8/kaava.egg-info/entry_points.txt +2 -0
  26. kaava-2026.6.8/kaava.egg-info/requires.txt +4 -0
  27. kaava-2026.6.8/kaava.egg-info/top_level.txt +1 -0
  28. kaava-2026.6.8/pyproject.toml +44 -0
  29. kaava-2026.6.8/setup.cfg +4 -0
  30. kaava-2026.6.8/test/test_builder.py +230 -0
  31. kaava-2026.6.8/test/test_cli.py +205 -0
  32. kaava-2026.6.8/test/test_discover.py +164 -0
  33. kaava-2026.6.8/test/test_doctor.py +225 -0
  34. kaava-2026.6.8/test/test_dotenv.py +127 -0
  35. kaava-2026.6.8/test/test_eject.py +135 -0
  36. kaava-2026.6.8/test/test_entrypoint.py +74 -0
  37. kaava-2026.6.8/test/test_enum.py +168 -0
  38. kaava-2026.6.8/test/test_examples.py +35 -0
  39. kaava-2026.6.8/test/test_explain.py +154 -0
  40. kaava-2026.6.8/test/test_fields.py +79 -0
  41. kaava-2026.6.8/test/test_fuzz.py +307 -0
  42. kaava-2026.6.8/test/test_overlays.py +253 -0
  43. kaava-2026.6.8/test/test_sources.py +130 -0
  44. kaava-2026.6.8/test/test_tracing.py +110 -0
  45. kaava-2026.6.8/test/test_tutorial.py +56 -0
  46. kaava-2026.6.8/test/test_validate.py +125 -0
  47. kaava-2026.6.8/test/test_validator.py +196 -0
@@ -0,0 +1 @@
1
+ Switzerland
@@ -0,0 +1 @@
1
+ EAR99
kaava-2026.6.8/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stefan Hagen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include COUNTRY-OF-ORIGIN
2
+ include EXPORT-CONTROL-CLASSIFICATION-NUMBER
3
+ include LICENSE
4
+ include README.md
5
+ include docs/kaava.1
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: kaava
3
+ Version: 2026.6.8
4
+ Summary: 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.
5
+ Author-email: Stefan Hagen <stefan@hagen.link>
6
+ Maintainer-email: Stefan Hagen <stefan@hagen.link>
7
+ License-Expression: MIT
8
+ Project-URL: Documentation, https://codes.dilettant.life/docs/kaava
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pyyaml
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # kaava
24
+
25
+ Load typed Python configuration from YAML, TOML, or JSON into dataclasses,
26
+ with validation, source tracing, environment-variable overlays, and a diagnostic CLI.
27
+
28
+ Requires Python 3.11 or later.
29
+ YAML support uses PyYAML; TOML and JSON use the standard library.
30
+
31
+ ## Install
32
+
33
+ ```
34
+ pip install kaava
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ Define your configuration shape as a plain dataclass and call `load()`.
40
+
41
+ ```python
42
+ import dataclasses
43
+ from kaava import conf_field, load
44
+
45
+
46
+ @dataclasses.dataclass
47
+ class DBConfig:
48
+ host: str = conf_field(description='database host')
49
+ port: int = conf_field(default=5432)
50
+
51
+
52
+ @dataclasses.dataclass
53
+ class AppConfig:
54
+ name: str = conf_field(description='application name')
55
+ db: DBConfig = conf_field(description='database connection')
56
+ debug: bool = conf_field(default=False)
57
+
58
+
59
+ cfg = load(AppConfig, 'config.yaml')
60
+ print(cfg.name, cfg.db.host, cfg.db.port)
61
+ ```
62
+
63
+ `config.yaml`:
64
+
65
+ ```yaml
66
+ name: myapp
67
+ db:
68
+ host: localhost
69
+ ```
70
+
71
+ Fields with no `default` or `default_factory` are required.
72
+ Nested dataclasses map to YAML mappings and are built recursively.
73
+ `load()` raises on the first error encountered; use `validate()` or `load_valid()` to collect all errors.
74
+
75
+ For a quick feature-by-feature tour, see `quickstart/README.md`.
76
+ For a step-by-step tutorial that builds a real command-line tool from scratch, see `tutorial/README.md`.
77
+
78
+ ## Field metadata
79
+
80
+ `conf_field()` extends `dataclasses.field()` with configuration-specific metadata.
81
+
82
+ - `default` / `default_factory` — the field's default value or factory
83
+ - `description` — shown in `explain()` output and in YAML templates produced by `eject()`
84
+ - `env` — explicit environment-variable name used by `env_overlay()`
85
+ - `secret` — when `True`, `explain()` masks the value; useful for passwords and API keys
86
+ - `validator` — a callable `(value) -> bool | str`; a falsy return or raised `ValueError` becomes a `ValidationError`
87
+
88
+ ## Sources
89
+
90
+ `load()` accepts one or more file paths (`str` or `pathlib.Path`) or `https://` URLs.
91
+ The format is inferred from the file extension (`.yaml` / `.yml`, `.toml`, `.json`); YAML is the default for unrecognised extensions.
92
+
93
+ ```python
94
+ cfg = load(AppConfig, 'base.yaml', 'prod.yaml')
95
+ ```
96
+
97
+ Sources are applied in order — later sources deep-merge over earlier ones.
98
+ A nested key in a later source only replaces its own subtree,
99
+ leaving sibling keys from earlier sources intact.
100
+
101
+ ### Auto-discovery
102
+
103
+ When called with no sources, `load()` searches for configuration in standard locations, in this order:
104
+
105
+ - `~/.config/kaava.toml` — user-level defaults
106
+ - `pyproject.toml`, `[tool.kaava]` section — project-level, if the section exists
107
+ - `kaava.toml`, `kaava.yaml`, or `kaava.json` in the working directory
108
+
109
+ Later entries override earlier ones via the same deep-merge.
110
+ Passing at least one explicit source skips discovery entirely.
111
+
112
+ ## Environment-variable overlays
113
+
114
+ Overlays are applied on top of all sources, last-wins.
115
+
116
+ ```python
117
+ from kaava import env_overlay, load
118
+
119
+ overlay = env_overlay(AppConfig, prefix='APP_')
120
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
121
+ ```
122
+
123
+ With `prefix='APP_'`, the field `db.host` maps to `APP_DB_HOST`.
124
+ An explicit `env=` annotation on a field always takes precedence over the derived name.
125
+ `env_overlay()` reads from `os.environ` by default; pass `environ=` to supply a custom mapping.
126
+
127
+ ### Dotenv files
128
+
129
+ ```python
130
+ from kaava import dotenv_environ, env_overlay, load
131
+
132
+ environ = dotenv_environ('.env')
133
+ overlay = env_overlay(AppConfig, environ=environ, prefix='APP_')
134
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
135
+ ```
136
+
137
+ `dotenv_environ()` parses `KEY=VALUE` pairs.
138
+ Blank lines and `#` comments are skipped.
139
+ An `export KEY=VALUE` prefix and surrounding quotes are stripped.
140
+
141
+ ### CLI arguments
142
+
143
+ ```python
144
+ from kaava import cli_from_namespace, load
145
+
146
+ overlay = cli_from_namespace(vars(args))
147
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
148
+ ```
149
+
150
+ `cli_from_namespace()` accepts an `argparse.Namespace` or a plain `dict`.
151
+ Dotted keys (`db.host`) are unflattened into the nested config structure automatically.
152
+
153
+ ## Collecting all errors
154
+
155
+ `validate()` returns every error as a list without raising, which is useful for CLI tools that want to report all problems at once.
156
+
157
+ ```python
158
+ from kaava import validate
159
+
160
+ errors = validate(AppConfig, 'config.yaml')
161
+ for e in errors:
162
+ print(e)
163
+ ```
164
+
165
+ `load_valid()` does the same collection but raises `ConfigErrors` at the end if any errors were found.
166
+
167
+ ## Source tracing and explain
168
+
169
+ `load_traced()` returns the config kaava together with a `SourceMap` recording where each field value came from.
170
+
171
+ ```python
172
+ from kaava import explain, load_traced
173
+
174
+ cfg, source_map = load_traced(AppConfig, 'base.yaml', 'prod.yaml')
175
+ print(explain(cfg, source_map))
176
+ ```
177
+
178
+ `explain()` renders a table of field paths, current values, sources, and descriptions.
179
+ Pass `show='non-default'` to show only fields whose value came from a source, not a dataclass default.
180
+
181
+ ## Diagnostics
182
+
183
+ `doctor()` probes every source, captures all load and validation errors without raising,
184
+ and reports which environment variables declared in the dataclass are set or unset.
185
+
186
+ ```python
187
+ from kaava import doctor
188
+
189
+ report = doctor(AppConfig, 'config.yaml', prefix='APP_')
190
+ print(report)
191
+ print('ok:', report.ok)
192
+ ```
193
+
194
+ The returned `DoctorReport` has fields `sources_ok`, `sources_failed`, `errors`,
195
+ `set_env`, `unset_env`, `kaava`, and `source_map`.
196
+ `report.ok` is `True` only when no sources failed and no errors were found.
197
+
198
+ ## Ejecting a template
199
+
200
+ `eject()` prints a YAML scaffold derived from the dataclass schema.
201
+ Required fields are annotated with `# required`; defaults, descriptions, and env-var names appear as inline comments.
202
+
203
+ ```python
204
+ from kaava import eject
205
+
206
+ print(eject(AppConfig))
207
+ ```
208
+
209
+ ## Companion CLI
210
+
211
+ The `kaava` command exposes the four diagnostic functions as subcommands.
212
+ The target dataclass is identified by a dotted import path of the form `module.path:ClassName`.
213
+
214
+ ```
215
+ kaava doctor myapp.config:AppConfig config.yaml
216
+ kaava explain myapp.config:AppConfig config.yaml
217
+ kaava validate myapp.config:AppConfig config.yaml
218
+ kaava eject myapp.config:AppConfig
219
+ ```
220
+
221
+ See `man kaava` for the full reference, including `--env-prefix`, `--dotenv`, and exit codes.
222
+
223
+ ## SBOM
224
+
225
+ Runtime dependency information is published in `docs/sbom/` in SPDX 3.0 (JSON-LD) and CycloneDX 1.6 (JSON) formats.
226
+ See `docs/sbom/README.md` for the component inventory and validation guide.
@@ -0,0 +1,204 @@
1
+ # kaava
2
+
3
+ Load typed Python configuration from YAML, TOML, or JSON into dataclasses,
4
+ with validation, source tracing, environment-variable overlays, and a diagnostic CLI.
5
+
6
+ Requires Python 3.11 or later.
7
+ YAML support uses PyYAML; TOML and JSON use the standard library.
8
+
9
+ ## Install
10
+
11
+ ```
12
+ pip install kaava
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ Define your configuration shape as a plain dataclass and call `load()`.
18
+
19
+ ```python
20
+ import dataclasses
21
+ from kaava import conf_field, load
22
+
23
+
24
+ @dataclasses.dataclass
25
+ class DBConfig:
26
+ host: str = conf_field(description='database host')
27
+ port: int = conf_field(default=5432)
28
+
29
+
30
+ @dataclasses.dataclass
31
+ class AppConfig:
32
+ name: str = conf_field(description='application name')
33
+ db: DBConfig = conf_field(description='database connection')
34
+ debug: bool = conf_field(default=False)
35
+
36
+
37
+ cfg = load(AppConfig, 'config.yaml')
38
+ print(cfg.name, cfg.db.host, cfg.db.port)
39
+ ```
40
+
41
+ `config.yaml`:
42
+
43
+ ```yaml
44
+ name: myapp
45
+ db:
46
+ host: localhost
47
+ ```
48
+
49
+ Fields with no `default` or `default_factory` are required.
50
+ Nested dataclasses map to YAML mappings and are built recursively.
51
+ `load()` raises on the first error encountered; use `validate()` or `load_valid()` to collect all errors.
52
+
53
+ For a quick feature-by-feature tour, see `quickstart/README.md`.
54
+ For a step-by-step tutorial that builds a real command-line tool from scratch, see `tutorial/README.md`.
55
+
56
+ ## Field metadata
57
+
58
+ `conf_field()` extends `dataclasses.field()` with configuration-specific metadata.
59
+
60
+ - `default` / `default_factory` — the field's default value or factory
61
+ - `description` — shown in `explain()` output and in YAML templates produced by `eject()`
62
+ - `env` — explicit environment-variable name used by `env_overlay()`
63
+ - `secret` — when `True`, `explain()` masks the value; useful for passwords and API keys
64
+ - `validator` — a callable `(value) -> bool | str`; a falsy return or raised `ValueError` becomes a `ValidationError`
65
+
66
+ ## Sources
67
+
68
+ `load()` accepts one or more file paths (`str` or `pathlib.Path`) or `https://` URLs.
69
+ The format is inferred from the file extension (`.yaml` / `.yml`, `.toml`, `.json`); YAML is the default for unrecognised extensions.
70
+
71
+ ```python
72
+ cfg = load(AppConfig, 'base.yaml', 'prod.yaml')
73
+ ```
74
+
75
+ Sources are applied in order — later sources deep-merge over earlier ones.
76
+ A nested key in a later source only replaces its own subtree,
77
+ leaving sibling keys from earlier sources intact.
78
+
79
+ ### Auto-discovery
80
+
81
+ When called with no sources, `load()` searches for configuration in standard locations, in this order:
82
+
83
+ - `~/.config/kaava.toml` — user-level defaults
84
+ - `pyproject.toml`, `[tool.kaava]` section — project-level, if the section exists
85
+ - `kaava.toml`, `kaava.yaml`, or `kaava.json` in the working directory
86
+
87
+ Later entries override earlier ones via the same deep-merge.
88
+ Passing at least one explicit source skips discovery entirely.
89
+
90
+ ## Environment-variable overlays
91
+
92
+ Overlays are applied on top of all sources, last-wins.
93
+
94
+ ```python
95
+ from kaava import env_overlay, load
96
+
97
+ overlay = env_overlay(AppConfig, prefix='APP_')
98
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
99
+ ```
100
+
101
+ With `prefix='APP_'`, the field `db.host` maps to `APP_DB_HOST`.
102
+ An explicit `env=` annotation on a field always takes precedence over the derived name.
103
+ `env_overlay()` reads from `os.environ` by default; pass `environ=` to supply a custom mapping.
104
+
105
+ ### Dotenv files
106
+
107
+ ```python
108
+ from kaava import dotenv_environ, env_overlay, load
109
+
110
+ environ = dotenv_environ('.env')
111
+ overlay = env_overlay(AppConfig, environ=environ, prefix='APP_')
112
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
113
+ ```
114
+
115
+ `dotenv_environ()` parses `KEY=VALUE` pairs.
116
+ Blank lines and `#` comments are skipped.
117
+ An `export KEY=VALUE` prefix and surrounding quotes are stripped.
118
+
119
+ ### CLI arguments
120
+
121
+ ```python
122
+ from kaava import cli_from_namespace, load
123
+
124
+ overlay = cli_from_namespace(vars(args))
125
+ cfg = load(AppConfig, 'config.yaml', overlays=[overlay])
126
+ ```
127
+
128
+ `cli_from_namespace()` accepts an `argparse.Namespace` or a plain `dict`.
129
+ Dotted keys (`db.host`) are unflattened into the nested config structure automatically.
130
+
131
+ ## Collecting all errors
132
+
133
+ `validate()` returns every error as a list without raising, which is useful for CLI tools that want to report all problems at once.
134
+
135
+ ```python
136
+ from kaava import validate
137
+
138
+ errors = validate(AppConfig, 'config.yaml')
139
+ for e in errors:
140
+ print(e)
141
+ ```
142
+
143
+ `load_valid()` does the same collection but raises `ConfigErrors` at the end if any errors were found.
144
+
145
+ ## Source tracing and explain
146
+
147
+ `load_traced()` returns the config kaava together with a `SourceMap` recording where each field value came from.
148
+
149
+ ```python
150
+ from kaava import explain, load_traced
151
+
152
+ cfg, source_map = load_traced(AppConfig, 'base.yaml', 'prod.yaml')
153
+ print(explain(cfg, source_map))
154
+ ```
155
+
156
+ `explain()` renders a table of field paths, current values, sources, and descriptions.
157
+ Pass `show='non-default'` to show only fields whose value came from a source, not a dataclass default.
158
+
159
+ ## Diagnostics
160
+
161
+ `doctor()` probes every source, captures all load and validation errors without raising,
162
+ and reports which environment variables declared in the dataclass are set or unset.
163
+
164
+ ```python
165
+ from kaava import doctor
166
+
167
+ report = doctor(AppConfig, 'config.yaml', prefix='APP_')
168
+ print(report)
169
+ print('ok:', report.ok)
170
+ ```
171
+
172
+ The returned `DoctorReport` has fields `sources_ok`, `sources_failed`, `errors`,
173
+ `set_env`, `unset_env`, `kaava`, and `source_map`.
174
+ `report.ok` is `True` only when no sources failed and no errors were found.
175
+
176
+ ## Ejecting a template
177
+
178
+ `eject()` prints a YAML scaffold derived from the dataclass schema.
179
+ Required fields are annotated with `# required`; defaults, descriptions, and env-var names appear as inline comments.
180
+
181
+ ```python
182
+ from kaava import eject
183
+
184
+ print(eject(AppConfig))
185
+ ```
186
+
187
+ ## Companion CLI
188
+
189
+ The `kaava` command exposes the four diagnostic functions as subcommands.
190
+ The target dataclass is identified by a dotted import path of the form `module.path:ClassName`.
191
+
192
+ ```
193
+ kaava doctor myapp.config:AppConfig config.yaml
194
+ kaava explain myapp.config:AppConfig config.yaml
195
+ kaava validate myapp.config:AppConfig config.yaml
196
+ kaava eject myapp.config:AppConfig
197
+ ```
198
+
199
+ See `man kaava` for the full reference, including `--env-prefix`, `--dotenv`, and exit codes.
200
+
201
+ ## SBOM
202
+
203
+ Runtime dependency information is published in `docs/sbom/` in SPDX 3.0 (JSON-LD) and CycloneDX 1.6 (JSON) formats.
204
+ See `docs/sbom/README.md` for the component inventory and validation guide.
@@ -0,0 +1,222 @@
1
+ .TH INSTANCE 1 "2026-06-09" "kaava 2026.6.8" "User Commands"
2
+ .SH NAME
3
+ kaava \- inspect and diagnose typed dataclass configuration
4
+ .SH SYNOPSIS
5
+ .B kaava
6
+ [\fB\-\-version\fR]
7
+ .I COMMAND
8
+ [\fIoptions\fR]
9
+ .I MODULE:CLASS
10
+ [\fISOURCE\fR ...]
11
+ .SH DESCRIPTION
12
+ .B kaava
13
+ is a companion CLI for the
14
+ .B kaava
15
+ Python library, which loads typed configuration from YAML, TOML, or JSON
16
+ sources into Python dataclasses.
17
+ .PP
18
+ The CLI is intended as an example for library users and as a diagnostic tool
19
+ during development and deployment.
20
+ It exposes four subcommands that exercise the core library surface:
21
+ .BR doctor ,
22
+ .BR explain ,
23
+ .BR validate ,
24
+ and
25
+ .BR eject .
26
+ .PP
27
+ Every subcommand takes a
28
+ .I MODULE:CLASS
29
+ argument that identifies the dataclass to operate on.
30
+ The class is imported at runtime using
31
+ .BR importlib.import_module ,
32
+ so the target package must be importable from the current environment.
33
+ .PP
34
+ Sources are applied in order;
35
+ later sources deep-merge over earlier ones.
36
+ Environment variable overlays and dotenv files are applied on top, last-wins.
37
+ .SH SUBCOMMANDS
38
+ .TP
39
+ .B doctor
40
+ Probe every source, capture all load and validation errors without raising,
41
+ and report which env vars are set or unset in the current environment.
42
+ Exits 0 if the configuration is fully valid, 1 if any error or source failure
43
+ was found.
44
+ .TP
45
+ .B explain
46
+ Load the configuration with source tracing and print a table showing each
47
+ field's current value, the source it came from, and its description (if any).
48
+ Exits 0 on success, 1 if the configuration could not be loaded.
49
+ .TP
50
+ .B validate
51
+ Collect all configuration errors without building the config kaava and
52
+ print them to standard error.
53
+ Exits 0 if the configuration is valid, 1 if errors were found.
54
+ .TP
55
+ .B eject
56
+ Print a YAML template scaffold derived from the dataclass schema.
57
+ Required fields are annotated with
58
+ .IR # required .
59
+ Default values, descriptions, env var names, and enum choices appear as
60
+ inline comments.
61
+ Exits 0.
62
+ .SH OPTIONS
63
+ The following options apply to
64
+ .BR doctor ,
65
+ .BR explain ,
66
+ and
67
+ .BR validate .
68
+ .B eject
69
+ takes only
70
+ .IR MODULE:CLASS .
71
+ .TP
72
+ .BI \-\-env\-prefix " PREFIX"
73
+ Derive environment variable names automatically from field paths using
74
+ .IR PREFIX .
75
+ For example,
76
+ .B \-\-env\-prefix APP_
77
+ maps
78
+ .B db.host
79
+ to
80
+ .BR APP_DB_HOST .
81
+ An explicit
82
+ .B env=
83
+ annotation on a field always takes precedence over the derived name.
84
+ .TP
85
+ .BI \-\-dotenv " PATH"
86
+ Read environment variables from a
87
+ .IR .env -format
88
+ file instead of the process environment.
89
+ The file is parsed as
90
+ .IB KEY = VALUE
91
+ pairs; blank lines and lines beginning with
92
+ .B #
93
+ are ignored;
94
+ .B export KEY=VALUE
95
+ prefix is stripped; quoted values are unquoted.
96
+ .SH MODULE:CLASS
97
+ The first positional argument to every subcommand is a dotted import path of
98
+ the form
99
+ .IB module.path : ClassName .
100
+ The module is imported with
101
+ .B importlib.import_module
102
+ and the class is looked up by name.
103
+ The target package must be installed or otherwise reachable on
104
+ .B sys.path
105
+ in the active Python environment.
106
+ .PP
107
+ Examples:
108
+ .RS
109
+ .PP
110
+ .B myapp.config:AppConfig
111
+ .br
112
+ .B myapp.settings:DatabaseConfig
113
+ .RE
114
+ .SH EXIT STATUS
115
+ .TP
116
+ .B 0
117
+ Success.
118
+ For
119
+ .B doctor
120
+ and
121
+ .BR validate ,
122
+ the configuration is fully valid.
123
+ For
124
+ .BR explain ,
125
+ the configuration was loaded and the table was printed.
126
+ For
127
+ .BR eject ,
128
+ the YAML template was printed.
129
+ .TP
130
+ .B 1
131
+ Configuration errors were found (
132
+ .BR doctor ,
133
+ .BR validate )
134
+ or the configuration could not be loaded (
135
+ .BR explain ).
136
+ .TP
137
+ .B 2
138
+ A usage error occurred: an invalid
139
+ .I MODULE:CLASS
140
+ spec, a module that could not be imported, a class not found in the module,
141
+ or a
142
+ .B \-\-dotenv
143
+ file that could not be read.
144
+ .SH EXAMPLES
145
+ Check whether a configuration is fully valid:
146
+ .PP
147
+ .RS
148
+ .B kaava doctor myapp.config:AppConfig config.yaml
149
+ .RE
150
+ .PP
151
+ Check with prefix-derived env vars (reads
152
+ .BR APP_DB_HOST ,
153
+ .BR APP_NAME ,
154
+ etc. from the environment):
155
+ .PP
156
+ .RS
157
+ .B kaava doctor myapp.config:AppConfig config.yaml \-\-env\-prefix APP_
158
+ .RE
159
+ .PP
160
+ Use a
161
+ .I .env
162
+ file instead of the process environment:
163
+ .PP
164
+ .RS
165
+ .B kaava doctor myapp.config:AppConfig config.yaml \-\-dotenv .env
166
+ .RE
167
+ .PP
168
+ Show all field values and their sources after loading:
169
+ .PP
170
+ .RS
171
+ .B kaava explain myapp.config:AppConfig config.yaml
172
+ .RE
173
+ .PP
174
+ Show only fields that were overridden from a source (not at their dataclass default):
175
+ .PP
176
+ .RS
177
+ .B kaava explain myapp.config:AppConfig config.yaml \-\-non\-default
178
+ .RE
179
+ .PP
180
+ Merge two sources (the second overrides the first where keys overlap):
181
+ .PP
182
+ .RS
183
+ .B kaava explain myapp.config:AppConfig base.yaml prod.yaml
184
+ .RE
185
+ .PP
186
+ Validate without building the kaava, collecting all errors:
187
+ .PP
188
+ .RS
189
+ .B kaava validate myapp.config:AppConfig config.yaml
190
+ .RE
191
+ .PP
192
+ Generate a YAML template for a config class:
193
+ .PP
194
+ .RS
195
+ .B kaava eject myapp.config:AppConfig
196
+ .RE
197
+ .PP
198
+ Redirect the template to a file:
199
+ .PP
200
+ .RS
201
+ .B kaava eject myapp.config:AppConfig > config.yaml
202
+ .RE
203
+ .SH FILES
204
+ .TP
205
+ .B quickstart/README.md
206
+ Feature-by-feature tour of the library API, with code snippets for each concept.
207
+ .TP
208
+ .B tutorial/README.md
209
+ Step-by-step tutorial that builds a complete command-line tool, one kaava feature
210
+ per step, with runnable code at every stage.
211
+ .SH SEE ALSO
212
+ The
213
+ .B kaava
214
+ library README documents the Python API:
215
+ .BR load (),
216
+ .BR validate (),
217
+ .BR doctor (),
218
+ .BR eject (),
219
+ .BR conf_field (),
220
+ and the overlay system.
221
+ .PP
222
+ .BR python (1)