CLI#
dature ships with a dature console script — installed automatically with the package, no extra dependencies. It provides two subcommands:
dature inspect— print the LoadReport (sources, field origins, merged data) for an existing configuration.dature validate— load the schema; exit0on success,1on validation failure,2on usage error.
Run dature --help for the top-level summary or dature <command> --help for per-command flags.
Nothing in the CLI is invented
Every flag below is a 1-to-1 reflection of an existing Python API:
--schema→ theschemaargument ofdature.load()--source type=X,k=v,...→ instantiating aSourcesubclass withk=vkeyword arguments- All global flags → matching parameters of
dature.load(), generated from its signature so they stay in sync automatically
The only CLI-specific flags are --field and --format (output filtering and rendering for inspect).
Quick start#
A schema module and a JSON config file:
Validate the config — exit 0, prints OK:
Inspect the load report in human-readable form:
inspect#
Loads the schema with debug=True, retrieves the LoadReport, and prints it.
usage: dature inspect [-h] --schema MODULE:ATTR --source SPEC
[--strategy {last_wins,first_wins,first_found,raise_on_conflict}]
[--skip-broken-sources] [--skip-invalid-fields]
[--expand-env-vars {disabled,default,empty,strict}]
[--secret-field-names SECRET_FIELD_NAMES]
[--mask-secrets] [--field DOTTED.PATH]
[--format {json,text}]
options:
-h, --help show this help message and exit
--schema MODULE:ATTR Import path to dataclass schema (e.g.
myapp.config:Settings).
--source SPEC Source spec: 'type=Class,k=v,k=v'. Repeatable (order
preserved). Use \, and \= to escape separators in
values.
--strategy {last_wins,first_wins,first_found,raise_on_conflict}
--skip-broken-sources
--skip-invalid-fields
--expand-env-vars {disabled,default,empty,strict}
--secret-field-names SECRET_FIELD_NAMES
--mask-secrets
--field DOTTED.PATH Filter origins and merged data by a dotted field path.
--format {json,text} Output format (default: json).
| Flag | Maps to | Description |
|---|---|---|
--field PATH | CLI-only | Filters field_origins and merged_data by a dotted path (e.g. db.port). Origins matching the path or prefixed by it are shown; merged data is narrowed to the value at that path. |
--format {json,text} | CLI-only | Output format. json (default) is stable and parseable; text is human-readable. |
JSON output#
{
"schema": "Settings",
"strategy": null,
"sources": [
{
"index": 0,
"file_path": "sources/config.json",
"loader_type": "json",
"raw_data": {
"db": {
"host": "localhost",
"port": 5432
}
}
}
],
"field_origins": [
{
"key": "db",
"value": {
"host": "localhost",
"port": 5432
},
"source_index": 0,
"source_file": "sources/config.json",
"source_loader_type": "json"
}
],
"merged_data": {
"db": {
"host": "localhost",
"port": 5432
}
}
}
Field filter#
Narrow the report to a single dotted path — db.port here resolves to a scalar in merged_data:
validate#
Runs load(*sources, schema=Schema, ...) and reports the result via exit code. Use it as the final gate in CI/CD.
usage: dature validate [-h] --schema MODULE:ATTR --source SPEC
[--strategy {last_wins,first_wins,first_found,raise_on_conflict}]
[--skip-broken-sources] [--skip-invalid-fields]
[--expand-env-vars {disabled,default,empty,strict}]
[--secret-field-names SECRET_FIELD_NAMES]
[--mask-secrets]
options:
-h, --help show this help message and exit
--schema MODULE:ATTR Import path to dataclass schema (e.g.
myapp.config:Settings).
--source SPEC Source spec: 'type=Class,k=v,k=v'. Repeatable (order
preserved). Use \, and \= to escape separators in
values.
--strategy {last_wins,first_wins,first_found,raise_on_conflict}
--skip-broken-sources
--skip-invalid-fields
--expand-env-vars {disabled,default,empty,strict}
--secret-field-names SECRET_FIELD_NAMES
--mask-secrets
| Exit code | Meaning |
|---|---|
0 | All sources loaded, schema validated — OK printed to stdout. |
1 | Loading or validation failed (invalid value, missing source file, merge conflict). Full error written to stderr. |
2 | Usage error — bad --schema import path, malformed --source spec, or class that is not a dature.Source subclass. |
Two sources merged with last_wins:
When validation fails, the error includes the field path, the offending value, and the source location:
| dature.errors.exceptions.DatureConfigError: Settings loading errors (1)
+-+---------------- 1 ----------------
| dature.errors.exceptions.FieldLoadError: [db.port] invalid literal for int() with base 10: 'not_a_number'
| ├── {"db": {"host": "localhost", "port": "not_a_number"}}
| │ ^^^^^^^^^^^^
| └── FILE 'sources/bad.json', line 1
+------------------------------------
--source spec#
Each --source is a serialised constructor call for a Source subclass. The CLI parses the spec, imports the class, and calls Class(**kwargs):
type(required) — import path of a class derived fromSource. Bothdature.JsonSourceanddature.sources.json_:JsonSourcework.- Other keys map directly to the constructor's keyword arguments — same names, same semantics. The full set is documented under
Source(base),FileSource(file-based), andFlatKeySource(env-style) plus per-class extras under Source Classes. Values are parsed as JSON first (e.g.skip_if_broken=true→True,port=8080→8080); strings that aren't valid JSON pass through unchanged.
--source is repeatable — order matters and is preserved (relevant for last_wins/first_wins strategies, see strategy).
Built-in source types#
type= | Class | Required kwargs |
|---|---|---|
dature.JsonSource | JSON | file= |
dature.Json5Source | JSON5 (extra [json5]) | file= |
dature.Yaml11Source, dature.Yaml12Source | YAML (extra [yaml]) | file= |
dature.Toml10Source, dature.Toml11Source | TOML (extra [toml]) | file= |
dature.IniSource | INI | file= |
dature.EnvSource | OS environment | — |
dature.EnvFileSource | .env-style file | file= (default .env) |
dature.DockerSecretsSource | /run/secrets-style dir | dir_= |
User-defined Source subclasses work too — pass the full import path.
Escaping commas and equals signs#
Use \, and \= to keep literal , / = inside a value:
"""Escaping commas inside ``--source`` values (``\\,``)."""
from dature import EnvSource
from dature.cli.parsing import parse_source_spec
klass, kwargs = parse_source_spec(r"type=dature.EnvSource,prefix=APP\,X_")
assert klass is EnvSource
assert kwargs == {"prefix": "APP,X_"}
--schema MODULE:ATTR#
Import path to the dataclass schema. Internally the CLI passes the resolved class to the schema parameter of dature.load(). Both : and final-dot separators work:
myapp.config:Settings— recommended, unambiguousmyapp.config.Settings— convenient when the class is exported at top level
Nested attributes after : are supported: myapp:Container.Settings.
Global flags#
These flags are generated from the signature of dature.load() at startup — same names (with _ → -), same types, same defaults. Adding a new parameter to load() adds the flag automatically; nothing in the CLI hard-codes the list.
| CLI flag | load() parameter | Notes |
|---|---|---|
--strategy | strategy | Choices: last_wins, first_wins, first_found, raise_on_conflict. See Merge Rules. |
--skip-broken-sources | skip_broken_sources | See Skipping Broken Sources. |
--skip-invalid-fields | skip_invalid_fields | See Skipping Invalid Fields. |
--expand-env-vars | expand_env_vars | Choices: disabled, default, empty, strict. See ENV Expansion. |
--secret-field-names | secret_field_names | Repeatable. See Masking. |
--mask-secrets | mask_secrets | See Masking. Also affects inspect output. |
Per-source overrides for parameters that exist on both load() and the Source constructor (e.g. expand_env_vars) can be passed inside --source, e.g. --source type=...,expand_env_vars=strict. The Source-level value takes priority — same precedence rules as in code.
Limitations#
The CLI exposes the common case. load() parameters that require Python objects — field_merges, field_groups, type_loaders, callable root_validators, explicit field_mapping — must be expressed in code. For those, use dature.load(...) directly. The CLI is intentionally not a substitute for the library.
Implementation note: dogfooding#
The dature console script parses its own arguments through ArgparseSource — the very class users instantiate to feed CLI arguments into their own configurations. The dataclass schema that the CLI loads into is built at runtime from the signature of load() (via dataclasses.make_dataclass over typing.get_type_hints(load)), so adding or changing a parameter on load() is automatically reflected in the CLI without any hand-written wiring. The CLI is not special code: it is dature using itself.