Skip to content

Add config_argument: user-supplied config file as argument defaults#48

Merged
mosquito merged 2 commits into
masterfrom
user-config-defaults
Jun 12, 2026
Merged

Add config_argument: user-supplied config file as argument defaults#48
mosquito merged 2 commits into
masterfrom
user-config-defaults

Conversation

@mosquito

Copy link
Copy Markdown
Owner

Summary

config_files= lets the developer preset defaults from files chosen at
construction time. The new Parser(config_argument="--config") adds a CLI
flag so the end user can point at a config file whose values become
argument defaults, resolved at invocation time:

class CLI(argclass.Parser):
    host: str = "localhost"
    port: int = 8080

parser = CLI(config_argument="--config")          # or ("-c", "--config", ...)
parser.parse_args(["--config", "prod.ini", "--port", "1234"])
# host comes from prod.ini, port from the CLI

Priority chain extends naturally:

declared defaults < config_files < config_argument file < env vars < CLI args

Implementation

Two-pass parsing (the classic argparse chicken-and-egg: defaults must be known
before parsing, but the config path arrives as an argument):

  1. A throwaway pre-scan parser — it neither prints nor exits — extracts only
    the config flag value from argv (syntax errors are left for the real parser
    to report with proper usage text).
  2. The file is loaded through the shared config_parser_class machinery
    (INI/JSON/TOML or a custom AbstractDefaultsParser) and layered over the
    constructor config defaults.
  3. The real parser is built as usual — required-relaxation, type-aware
    conversion, group/nested-group sections, and --help defaults all come
    from the existing defaults pipeline.

Details:

  • An explicitly passed path that is missing or unparsable raises
    ConfigurationError — unlike config_files, which stays a lenient search
    list.
  • The runtime defaults layer lives only for the duration of one
    parse_args() call (no staleness between parses).
  • New Parser.loaded_config_files property reports applied files in priority
    order.
  • No cascade into subparsers (consistent with config_files).

Also fixes a related pre-existing bug in ConfigAction (argclass.Config()):
an explicit --config file was merged before search_paths, so preset
files silently overrode the user's explicit choice. The explicit file now
wins.

Tests & docs

  • 19 new tests (tests/test_config_argument.py): the priority chain
    pair-by-pair, required-satisfied-by-config, nested group sections, JSON
    parser class, aliases/=-form, help output, error cases, re-parse
    isolation, and the ConfigAction ordering fix.
  • Docs: new section in config-files.md (executable example), README example,
    api.md cross-references, Parser.__init__ docstring (flows into the API
    reference via autodoc), feature bullet in index.md, pitfalls table row.

Verification

  • 725 tests green on Python 3.10–3.14; doc blocks executable and passing.
  • ruff check/format clean; mypy clean (pre-existing tomllib baseline only);
    coverage at the 100% gate; Sphinx builds without warnings.

mosquito added 2 commits June 13, 2026 01:22
config_files= lets the developer preset defaults from files chosen at
construction time. The new Parser(config_argument="--config") adds a
CLI flag so the END USER can point at a config file whose values
become argument defaults, resolved at invocation time.

Implementation is two-pass parsing: a throwaway pre-scan parser (that
neither prints nor exits) extracts only the config path from argv;
the file is loaded through the shared config_parser_class machinery
and layered over the constructor config defaults; then the real
parser is built — so required-relaxation, type-aware conversion,
group sections, and --help defaults all come from the existing
defaults pipeline.

Priority chain extends naturally:
  declared defaults < config_files < config_argument file < env < CLI

Properties of the feature:
- accepts one flag or several aliases: config_argument=("-c", "--config")
- an explicitly passed path that is missing or malformed raises
  ConfigurationError (config_files stays a lenient search list)
- no cascade into subparsers (consistent with config_files)
- runtime defaults live only for the duration of one parse_args call
- new Parser.loaded_config_files property reports applied files in
  priority order

Also fixes a related pre-existing bug in ConfigAction: an explicit
--config file was merged BEFORE search_paths, so preset files
silently overrode the user's explicit choice; the explicit file now
wins.
Python 3.14 argparse colorizes usage/help output and CI sets
FORCE_COLOR=1, so ANSI escapes broke substring assertions on help
text (the new test_flag_appears_in_help was the first such assert).
PYTHON_COLORS takes precedence over FORCE_COLOR, making captured
output deterministic everywhere.
@mosquito mosquito merged commit 8e6993b into master Jun 12, 2026
13 checks passed
@mosquito mosquito deleted the user-config-defaults branch June 12, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant