Skip to content

Releases: mosquito/argclass

1.11.1

Choose a tag to compare

@mosquito mosquito released this 19 Sep 21:32
578596c

What's Changed

  • Config generation and config files cover subparsers by @mosquito in #52

Full Changelog: 1.10.3...1.11.1

1.11.0

Choose a tag to compare

@mosquito mosquito released this 19 Sep 21:13
578596c

What's Changed

  • Config generation and config files cover subparsers by @mosquito in #52

Full Changelog: 1.10.3...1.11.0

1.10.3

Choose a tag to compare

@mosquito mosquito released this 11 Sep 23:26
66b5465

What's Changed

  • docs: add config mechanism navigator table to config-files by @mosquito in #49
  • docs: migrate documentation to the Diátaxis framework by @mosquito in #50
  • feat: human-friendly config/env templates + allow metaclass-named fields by @mosquito in #51

Full Changelog: 1.10.2...1.10.3

1.10.2

Choose a tag to compare

@mosquito mosquito released this 12 Jun 23:29
8e6993b

What's Changed

  • Add config_argument: user-supplied config file as argument defaults by @mosquito in #48

Full Changelog: 1.10.1...1.10.2

1.10.1

Choose a tag to compare

@mosquito mosquito released this 12 Jun 23:04
e0404fc

What's Changed

  • Fix 13 audit findings; per-instance group/subparser copies by @mosquito in #47

Full Changelog: 1.10.0...1.10.1

1.10.0

Choose a tag to compare

@mosquito mosquito released this 12 Jun 21:41
b4d7f01

What's Changed

  • Fix Argument typing, modernize to 3.10+, reserve internal attr names by @mosquito in #46

Full Changelog: 1.9.2...1.10.0

1.9.2

Choose a tag to compare

@mosquito mosquito released this 21 May 21:56
d4998dc

What's Changed

Full Changelog: 1.9.1...1.9.2

1.9.1

Choose a tag to compare

@mosquito mosquito released this 21 May 21:06

Fix docs

Full Changelog: 1.9.0...1.9.1

1.9.0

Choose a tag to compare

@mosquito mosquito released this 21 May 20:20
4c755e8

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 | None

Custom 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.
  • GenerateConfigAction runs mid-parse: it reads from the live argparse Namespace first (so CLI flags processed before --generate-config land in the dump), then falls back to env vars and class defaults.
  • Programmatic generator.dump_to_string(parser) after a successful parse_args gives 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:

  1. Inherit from argclass.NonConfigAction (cleanest for new actions).
  2. Set __emit_config__ = False on 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 (matches EnumArgument — accepts both upper and lower case via lowercase=True).
  • set / frozenset → emitted as sorted list so INI's ast.literal_eval and JSON/TOML's array syntax can read it back.
  • Path / PurePath → emitted as str.
  • SecretString → emitted as the underlying string (treat the output file as credential-bearing).
  • list[T] survives every format end-to-end.
  • Optional[T] = None is dropped (each format would either reject None or round-trip it as an empty string; dropping lets the reload fall back to the argument's own default).

Other notable changes

  • INIDefaultsParser no longer leaks [DEFAULT] keys into nested sections. A top-level host = root cannot leak into [inner].host when the group's own default is None. The own_section_items() helper centralises the one place we reach into configparser._sections.
  • coerce_env_default(raw, argument) in argclass.utils is the single source of truth for env-string → typed-value conversion shared by Parser._add_argument and the dump-time field walker.
  • The parser now exposes an out-of-band get_argclass_parser() helper in argclass.parser (no mutation of argparse.ArgumentParser attributes) 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-config mid-flight, bulk pipelines).
  • ConfigField documented in the API reference.
  • README, docs/arguments.md, and docs/llms.txt updated 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.
  • GenerateConfigAction runs synchronously during argparse; CLI args appearing AFTER --generate-config are not reflected in the dump.

Full Changelog: 1.8.1...1.9.0

1.8.1

Choose a tag to compare

@mosquito mosquito released this 21 May 18:24

Add more docs examples

Full Changelog: 1.8.0...1.8.1