CLI Argument Parsing

The configuration system supports parsing command-line interface (CLI) arguments into commands, arguments and options using the format:

my-app [<command>] [<arguments>] [--<option-1-name> <option-1-value>] [--<option-2-name>]

Where:

Command-line arguments are defined as an argsparse spec in a YAML file (e.g., args.yaml). ConfigArgsParser reads this spec, resolves help and default expressions against the Config it is given and returns a DictConfig.

The values of the DictConfig are set based on the config key for each argument or option, with the command stored under the path <key>.command (where key is passed into ConfigArgsParser and defaults to None). This DictConfig can then be added to the override layer of the configuration using add_overrides.

Arguments and options automatically:

If the command contains an argument or option that is invalid, or does not contain a required argument or option, a short usage line is printed with an error message.

If -h or --help is passed, the complete help for the current command will be displayed, with the epilog from the arguments configuration for that command appended at the end. The epilog can be used to include examples or other text which further clarifies the command.

Argument Spec

The top-level spec declares a default_command, global_options (flags accepted before or after any command), and commands, with the latter usually split into per-command files via includes:

# args.yaml
includes:
  - commands/run.yaml

cli:
  default_command: run
  global_options:
    verbose:
      short_name: v
      type: bool
      action: store_true
      help: "Enable verbose logging"
      config: logging.verbose

Each command declares positional arguments and named options. This run command exercises the full grammar:

# commands/run.yaml
cli:
  commands:
    run:
      description: Run the application server.
      epilog: |
        Examples:
          myapp run ./app.yaml
          myapp run ./app.yaml --host 0.0.0.0 --port 8080 --workers 4 --reload

      arguments:                 # positional, in declaration order (always required when defined)
        config_file:
          type: str
          help: "Path to the app config file"
          config: run.config_file

      options:                   # named flags
        host:
          type: str
          default: localhost
          help: "Interface to bind (default: ${server.host})"
          config: server.host

        port:
          short_name: p          # also accepts -p
          type: int
          default: 8080
          help: "Port to listen on"
          config: server.port

        workers:
          type: int
          required: true         # options are optional unless required
          help: "Number of worker processes"
          config: server.workers

        log-level:
          type: str
          choices: [debug, info, warning, error]
          default: info
          help: "Logging verbosity"
          config: logging.level

        reload:
          type: bool
          action: store_true     # boolean flag; consumes no value
          help: "Restart workers when files change"
          config: server.reload

The argument parser reads the YAML spec file and uses it to parse the command and its arguments and options. The parsed values are then saved to the configuration at the path specified by the config key for each argument or option.

For example, given this code:

from .global_config import config

args_config = ConfigArgsParser.parse(config=config, spec="args.yaml", key="cli")
config.add_overrides(args_config)

Running myapp run ./app.yaml -p 9000 --reload would:

  • Store the resolved comment name at cli.command (the key you passed to parse).
  • Store the positional ./app.yaml argument at run.config_file.
  • Store the option flags -p 9000 and --reload at server.port and server.reload, respectively.
  • Store the default values defined for the option flags host and log-level (i.e., localhost and info) into server.host and logging.level, respectively.

These values would then all be added into the overrides layer, overriding all other values of the configuration.

Argument Grammar

Each entry under arguments (positional) or options / global_options (named flags) accepts:

  • config: dot-path where the parsed value is written (omit it and the value is parsed but not stored)
  • type: str (default), int, float, bool, or a fully-qualified type path (resolved via the import machinery)
  • help: help text; may contain ${...} expressions resolved against the provided config.
  • default: value used when the flag is absent
  • choices: list of allowed values
  • action: an argparse action such as store_true / store_false (boolean flags consume no value)
  • required: whether the option is required (options only; positionals are always required)
  • short_name: a single-character alias, e.g., p-p (options only)

Each command entry (a value under commands) accepts:

  • description: the command’s full help text, shown in <command> --help
  • help: a one-line summary shown in the parent command listing (falls back to description)
  • epilog: text appended after the help, e.g., usage examples
  • arguments: positional arguments (a mapping of name to the argument grammar above)
  • options: named flags (a mapping of name to the argument grammar above)
  • commands: nested subcommands, one level deep; each value is itself a command entry
  • default_command: the subcommand injected when the user omits it (only meaningful alongside commands)

The resolved command is written to {key}.command (subcommands as command:subcommand).

When not using commands, set the default_command to default and define your arguments and options under cli.commands.default.