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.
- kaava-2026.6.8/COUNTRY-OF-ORIGIN +1 -0
- kaava-2026.6.8/EXPORT-CONTROL-CLASSIFICATION-NUMBER +1 -0
- kaava-2026.6.8/LICENSE +21 -0
- kaava-2026.6.8/MANIFEST.in +5 -0
- kaava-2026.6.8/PKG-INFO +226 -0
- kaava-2026.6.8/README.md +204 -0
- kaava-2026.6.8/docs/kaava.1 +222 -0
- kaava-2026.6.8/kaava/__init__.py +93 -0
- kaava-2026.6.8/kaava/__main__.py +5 -0
- kaava-2026.6.8/kaava/_builder.py +201 -0
- kaava-2026.6.8/kaava/_cli.py +152 -0
- kaava-2026.6.8/kaava/_discover.py +47 -0
- kaava-2026.6.8/kaava/_doctor.py +118 -0
- kaava-2026.6.8/kaava/_eject.py +73 -0
- kaava-2026.6.8/kaava/_errors.py +25 -0
- kaava-2026.6.8/kaava/_explain.py +53 -0
- kaava-2026.6.8/kaava/_fields.py +19 -0
- kaava-2026.6.8/kaava/_merge.py +34 -0
- kaava-2026.6.8/kaava/_overlays.py +83 -0
- kaava-2026.6.8/kaava/_sources.py +60 -0
- kaava-2026.6.8/kaava/py.typed +0 -0
- kaava-2026.6.8/kaava.egg-info/PKG-INFO +226 -0
- kaava-2026.6.8/kaava.egg-info/SOURCES.txt +45 -0
- kaava-2026.6.8/kaava.egg-info/dependency_links.txt +1 -0
- kaava-2026.6.8/kaava.egg-info/entry_points.txt +2 -0
- kaava-2026.6.8/kaava.egg-info/requires.txt +4 -0
- kaava-2026.6.8/kaava.egg-info/top_level.txt +1 -0
- kaava-2026.6.8/pyproject.toml +44 -0
- kaava-2026.6.8/setup.cfg +4 -0
- kaava-2026.6.8/test/test_builder.py +230 -0
- kaava-2026.6.8/test/test_cli.py +205 -0
- kaava-2026.6.8/test/test_discover.py +164 -0
- kaava-2026.6.8/test/test_doctor.py +225 -0
- kaava-2026.6.8/test/test_dotenv.py +127 -0
- kaava-2026.6.8/test/test_eject.py +135 -0
- kaava-2026.6.8/test/test_entrypoint.py +74 -0
- kaava-2026.6.8/test/test_enum.py +168 -0
- kaava-2026.6.8/test/test_examples.py +35 -0
- kaava-2026.6.8/test/test_explain.py +154 -0
- kaava-2026.6.8/test/test_fields.py +79 -0
- kaava-2026.6.8/test/test_fuzz.py +307 -0
- kaava-2026.6.8/test/test_overlays.py +253 -0
- kaava-2026.6.8/test/test_sources.py +130 -0
- kaava-2026.6.8/test/test_tracing.py +110 -0
- kaava-2026.6.8/test/test_tutorial.py +56 -0
- kaava-2026.6.8/test/test_validate.py +125 -0
- 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.
|
kaava-2026.6.8/PKG-INFO
ADDED
|
@@ -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.
|
kaava-2026.6.8/README.md
ADDED
|
@@ -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)
|