1.9.0
Config file generation
argclass can now write config files for a parser — the symmetric inverse of the existing config_files= reading. Useful for
scaffolding sample configs, snapshotting deployed state, converting between formats, or exporting env-var listings.
Built-in generators
| Class | Output | Help comments |
|---|---|---|
INIConfigGenerator |
INI | ; <text> |
TOMLConfigGenerator |
TOML | # <text> |
JSONConfigGenerator |
JSON | (dropped — no comment syntax) |
EnvConfigGenerator |
.env |
# <text> |
TOML emission is hand-rolled (~50 lines) so the zero-dependency guarantee stays intact.
Quick usage
import argclass
class Database(argclass.Group):
host: str = "localhost"
port: int = 5432
class CLI(argclass.Parser):
debug: bool = False
db: Database = Database()
# Built-in argparse Action: --generate-config FILE (or '-' for stdout)
generate = argclass.Argument(
"--generate-config",
action=argclass.GenerateConfigAction,
generator=argclass.INIConfigGenerator,
)
print(argclass.TOMLConfigGenerator().dump_to_string(CLI()))
print(argclass.EnvConfigGenerator().dump_to_string(
CLI(auto_env_var_prefix="APP_"),
))End users get a working myapp --generate-config /etc/myapp.ini flag for free; - writes to stdout.
Architecture
A single tree-walk yields ConfigField records; every generator consumes the same materialised sequence. render(fields) is the only thing subclasses need to override.
@dataclass(frozen=True)
class ConfigField:
attr_path: tuple[str, ...] # ("endpoint", "credentials", "username")
cli_path: tuple[str, ...] # respects per-group prefix= overrides
dest: str # "endpoint_credentials_username"
argument: TypedArgument
target: Any # owning Parser/Group instance
value: Any # normalised, round-trip safe
env_var: str | None
help: str | NoneCustom format example::
class KeyValueGenerator(argclass.ConfigGenerator):
extension = ".kv"
def render(self, fields):
return "\n".join(
f"{'.'.join(f.attr_path)}={f.value}"
for f in fields
if f.value is not None
) + "\n"Sources reflected in the dump
The dump captures the parser's CURRENT resolved state:
- Defaults < config file < env var < CLI priority is preserved.
GenerateConfigActionruns mid-parse: it reads from the live argparseNamespacefirst (so CLI flags processed before--generate-configland in the dump), then falls back to env vars and class defaults.- Programmatic
generator.dump_to_string(parser)after a successfulparse_argsgives a full post-resolution snapshot.
This makes it trivial to convert between formats (load an INI, dump as TOML), materialise an env-based config to a file, or snapshot a deployed app's effective configuration.
NonConfigAction: opting out of dumps
"Fire and exit" actions (--version, --check-updates, --generate-config itself) make no sense in a config file. argclass skips them in two equivalent ways:
- Inherit from
argclass.NonConfigAction(cleanest for new actions). - Set
__emit_config__ = Falseon an existing action class (useful when you already inherit from a third-party Action).
argparse's built-in --help / --version are recognised automatically.
Round-trip fidelity
Enum/IntEnum→ emitted as.name(matchesEnumArgument— accepts both upper and lower case vialowercase=True).set/frozenset→ emitted as sortedlistso INI'sast.literal_evaland JSON/TOML's array syntax can read it back.Path/PurePath→ emitted asstr.SecretString→ emitted as the underlying string (treat the output file as credential-bearing).list[T]survives every format end-to-end.Optional[T] = Noneis dropped (each format would either rejectNoneor round-trip it as an empty string; dropping lets the reload fall back to the argument's own default).
Other notable changes
INIDefaultsParserno longer leaks[DEFAULT]keys into nested sections. A top-levelhost = rootcannot leak into[inner].hostwhen the group's own default isNone. Theown_section_items()helper centralises the one place we reach intoconfigparser._sections.coerce_env_default(raw, argument)inargclass.utilsis the single source of truth for env-string → typed-value conversion shared byParser._add_argumentand the dump-time field walker.- The parser now exposes an out-of-band
get_argclass_parser()helper inargclass.parser(no mutation ofargparse.ArgumentParserattributes) so custom actions can recover the argclass parser.
Public API additions
argclass.ConfigField
argclass.ConfigGenerator
argclass.INIConfigGenerator
argclass.JSONConfigGenerator
argclass.TOMLConfigGenerator
argclass.EnvConfigGenerator
argclass.GenerateConfigAction
argclass.NonConfigAction
Plus introspection helpers in argclass.emit: iter_config_fields, iter_subtree_fields, fields_to_nested_dict, group_fields_by_section, should_emit, current_value, derive_env_var, group_cli_segment, normalize_value, escape_inline_string. Power-users only — not re-exported from the top-level namespace.
Interactive demo
python -m argclass genconfig --generate-ini -
python -m argclass genconfig --generate-toml /tmp/myapp.toml
python -m argclass genconfig --port 9999 --tags a b c --generate-json -
DEMO_HOST=prod python -m argclass genconfig --generate-env -Documentation
- New dedicated page: Generating Config Files.
- Includes a format-migration guide (one-shot script,
--generate-configmid-flight, bulk pipelines). ConfigFielddocumented in the API reference.- README,
docs/arguments.md, anddocs/llms.txtupdated with cross-references.
Limitations (intentional, documented)
- Subparsers are skipped from dumps — each subparser can be dumped separately.
- Secrets emit actual values; treat output files as credential-bearing.
- JSON has no comments, so help text is dropped in that format.
GenerateConfigActionruns synchronously during argparse; CLI args appearing AFTER--generate-configare not reflected in the dump.
Full Changelog: 1.8.1...1.9.0