Configuration files

The structure of the configuration file is derived from the CLI’s parameters and their types. You never write a data structure to mirror the CLI.

Tip

After loading, the resolved file path, the full parsed document, and (when a config_schema is set) the typed app section are exposed on ctx.meta as CONF_SOURCE, CONF_FULL, and TOOL_CONFIG. With cascade=True, CONF_SOURCES additionally lists every file that was loaded. See the available keys table to read them from your own callbacks.

Resolving a configuration file

Before any value is read, Click Extra decides which file, if any, provides the configuration. An explicit --config (or its environment variable or interactive prompt) wins outright. Otherwise autodiscovery applies: pyproject.toml is searched from the current directory up to the VCS root, then the app-dir search pattern takes over. The first file that parses to a non-empty mapping is used, with no merging across files.

        flowchart TD
    start(["@config_option resolves a pattern"]) --> nc{"autodiscovery disabled?"}
    nc -->|yes| skip["Skip loading, use bare defaults"]
    nc -->|no| exp{"--config, env or prompt set?"}
    exp -->|"no, auto-discover"| pyp{"pyproject.toml format enabled?"}
    pyp -->|yes| cwd{"tool.cli table in a pyproject.toml, CWD up to VCS root?"}
    cwd -->|yes| usepyp["Use that tool.cli section"]
    cwd -->|no| search["Search files matching the pattern, try formats in order"]
    pyp -->|no| search
    exp -->|yes| search
    search --> parse{"a file parses to a non-empty config?"}
    parse -->|yes| win["First match wins, no merging"]
    parse -->|"no, explicit"| fail["Exit with code 2"]
    parse -->|"no, auto-discover"| defaults["Use bare defaults"]
    

Once a file is selected, its values feed into the precedence chain below: environment variables, CLI parameters, and interactive prompts all override what the file provides.

Standalone option

The @config_option decorator provided by Click Extra can be used as-is with vanilla Click:

from click import group, option, echo
from click_extra import config_option

@group(context_settings={"show_default": True})
@option("--dummy-flag/--no-flag")
@option("--my-list", multiple=True)
@config_option
def my_cli(dummy_flag, my_list):
    echo(f"dummy_flag    is {dummy_flag!r}")
    echo(f"my_list       is {my_list!r}")

@my_cli.command
@option("--int-param", type=int, default=10)
def subcommand(int_param):
    echo(f"int_parameter is {int_param!r}")

The code above is saved in a file named my_cli.py. It produces the following help screen:

$ my-cli --help
Usage: my-cli [OPTIONS] COMMAND [ARGS]...

Options:
  --dummy-flag / --no-flag  [default: no-flag]
  --my-list TEXT
  --config LOCATION         Location of the configuration file. Supports local
                            path with glob patterns or remote URL.  [default:
                            ~/.config/my-cli/]
  --help                    Show this message and exit.

Commands:
  subcommand

The help screen names the default location of the configuration file ([default: ~/.config/my-cli/]). This improves discoverability, and makes sysadmins happy, especially those not familiar with your CLI. The files searched in that folder depend on the formats you enabled.

A bare call returns:

$ my-cli subcommand
dummy_flag    is False
my_list       is ()
int_parameter is 10

A TOML file in the application folder changes the CLI’s defaults. Here is what ~/.config/my-cli/config.toml contains:

~/.config/my-cli/config.toml
# My default configuration file.
top_level_param = "is_ignored"

[my-cli]
extra_value = "is ignored too"
dummy_flag = true                                  # New boolean default.
my_list = ["item 1", "item #2", "Very Last Item!"]

[garbage]
# An empty random section that will be skipped.

[my-cli.subcommand]
int_param = 3
random_stuff = "will be ignored"

In the file above, note:

  • The default configuration base path, which is OS-dependent (the ~/.config/my-cli/ path here is for Linux).

  • The app’s folder (/my-cli/), built from the script’s name (my_cli.py).

  • The top-level config section ([my-cli]), based on the CLI’s group ID (def my_cli()).

  • The extra comments, sections and values, all silently ignored.

The configuration file is read and changes the defaults:

$ my-cli subcommand
dummy_flag    is True
my_list       is ('item 1', 'item #2', 'Very Last Item!')
int_parameter is 3

Key spelling

Configuration keys address CLI parameters by name, in either of two spellings:

  • kebab-case is the canonical presentation: it matches the spelling of the CLI flags and the convention of TOML and YAML files. It is what --export-config emits.

  • snake_case is the parameter’s internal ID, which Click derives from the flag by replacing hyphens with underscores: the --dummy-flag option is the dummy_flag parameter. This ID is what --params reports, and what roots the auto-generated environment variable (CLI_DUMMY_FLAG), since neither Python identifiers nor environment variables can carry dashes.

Both spellings resolve to the same parameter: dummy-flag and dummy_flag both set --dummy-flag. When the two coexist in a file, the last one in file order wins and a warning names both.

Case does not have to match either. Click folds case when it derives a parameter name from a flag, so --Dummy-Flag would also be the dummy_flag parameter, and a key spelled Dummy-Flag or DUMMY_FLAG reaches it too.

Caution

Folding is how the key is found, never how the parameter is named. Click takes a third positional declaration verbatim, so a parameter declared @option("--flag", "Dummy_Flag") really is named Dummy_Flag, and its config key keeps that case. A key is matched against the names a CLI declares, so a CLI that declares both dummy_flag and Dummy_Flag leaves DUMMY_FLAG addressing neither: nothing tells the two apart, and the key is skipped with a warning.

Note

Click also accepts snake_case flags: --my_option is legal and derives the same my_option parameter ID as --my-option would. For such a CLI the canonical config key is still the kebab-cased my-option, which loads back to the same parameter.

Decoupling the flag from the configuration key

Click’s third positional declaration names the parameter explicitly, decoupling it from the flags. This is handy for repeatable options, where the flag names one occurrence but the configuration key holds the whole collection: keep the flag singular and pluralize the parameter, so the config key, the environment variable and the callback argument all read as the list they are.

from click_extra import command, echo, option

@command
@option("--hash-header", "hash_headers", multiple=True)
def scanner(hash_headers):
    echo(f"headers = {hash_headers!r}")

The --hash-header flag is repeated once per value:

$ scanner --hash-header Date --hash-header From
headers = ('Date', 'From')

While the configuration key, shown here by exporting the values just set, is the plural hash-headers list:

$ scanner --hash-header Date --hash-header From --export-config toml
[scanner]
hash-headers = ["Date", "From"]
time = false
accessible = false
color = "auto"
no-color = false
progress = true
theme = "dark"
table-format = "rounded-outline"
verbosity = "WARNING"
verbose = 0
quiet = 0
debug = false
tree = false
man = false
# help-format =

The environment variable follows the parameter ID too, so a single SCANNER_HASH_HEADERS value feeds the whole list.

Dotted keys

Configuration files support dotted keys as a shorthand for nested structures. Instead of writing:

Nested structure
[my-cli.subcommand]
int_param = 3

You can write:

Dotted key equivalent
[my-cli]
"subcommand.int_param" = 3

Both forms are equivalent. You can also freely mix them in the same file:

Mixed dotted and nested keys in JSON
{
    "my-cli": {
        "dummy_flag": true,
        "subcommand.int_param": 3,
        "subcommand": {
            "other_param": "value"
        }
    }
}

Dotted keys are expanded into nested dicts and deep-merged before the configuration is applied. This works across all supported formats, and at any nesting depth (for example, "subcommand.nested.option" expands to three levels).

Hint

This is especially handy in formats like JSON that have no native section syntax, letting you keep a flat structure when the nesting would be excessive.

Merge rules

When dotted keys and nested structures target the same leaf, the last one in file order wins:

Last value wins
{
    "my-cli": {
        "subcommand": {"int_param": 3},
        "subcommand.int_param": 77
    }
}

Here int_param resolves to 77 because the dotted key appears after the nested one.

Conflicts

A conflict occurs when the same key is used as both a scalar and a namespace. For example:

Conflicting types on the same key
{
    "my-cli": {
        "subcommand": "some_value",
        "subcommand.int_param": 3
    }
}

Here subcommand is a plain string, but subcommand.int_param requires it to be a dict. By default, Click Extra logs a warning and the last value wins: in this case, subcommand becomes {"int_param": 3}, silently dropping "some_value".

In strict mode, conflicts and invalid dotted keys raise a ValueError instead of being silently resolved.

The same conflict detection applies at deeper levels:

Deep conflict
{
    "my-cli": {
        "subcommand.int_param.nested": 1,
        "subcommand.int_param": 2
    }
}

Here int_param is set to both {"nested": 1} (via the first key) and 2 (via the second). A warning is logged and int_param resolves to 2.

Note

Most formats prevent these conflicts at parse time (TOML rejects a key used as both a scalar and a table, YAML forbids duplicate keys), so in practice this mainly affects JSON.

Invalid dotted keys

Dotted keys with empty segments (leading, trailing, or consecutive dots) are skipped with a warning:

Invalid keys that are skipped
{
    "my-cli": {
        ".option": 1,
        "option.": 2,
        "sub..option": 3
    }
}

All three keys above are ignored. Use --verbosity WARNING or higher to see the warnings. In strict mode, they raise a ValueError.

Precedence

The loader fetches values in the following precedence order:

        flowchart TD
    P["Interactive prompt"] -->|unset| C["CLI parameters"]
    C -->|unset| E["Environment variables"]
    E -->|unset| F["Configuration file"]
    F -->|unset| D["Defaults"]
    

Each parameter takes the first value set in that chain.

Configuration file values are loaded into Click’s default_map, so they are reported as DEFAULT_MAP and sit below environment variables in the hierarchy.

Inline parameters take priority over the file’s defaults:

$ my-cli subcommand --int-param 555
dummy_flag    is True
my_list       is ('item 1', 'item #2', 'Very Last Item!')
int_parameter is 555

Get configuration values

The resolved values are merged into the context’s default_map. Only values matching a CLI parameter are kept and passed as defaults. All others are silently ignored.

The full configuration stays accessible in the context’s meta attribute:

from click_extra import option, echo, pass_context, command, config_option


@command
@option("--int-param", type=int, default=10)
@config_option
@pass_context
def my_cli(ctx, int_param):
    echo(f"Configuration location: {ctx.meta['click_extra.conf_source']}")
    echo(f"Full configuration: {ctx.meta['click_extra.conf_full']}")
    echo(f"Default values: {ctx.default_map}")
    echo(f"int_param is {int_param!r}")
./conf.toml
[my-cli]
int_param = 3
random_stuff = "will be ignored"

[garbage]
dummy_flag = true
$ my-cli --config ./conf.toml --int-param 999
Load configuration matching ./conf.toml
Configuration location: /home/me/conf.toml
Full configuration: {'my-cli': {'int_param': 3, 'random_stuff': 'will be ignored'}, 'garbage': {'dummy_flag': True}}
Default values: {'int_param': 3}
int_parameter is 999

Hint

Variables in meta are presented in their original Python type:

  • click_extra.conf_source is either a normalized Path or URL object

  • click_extra.conf_full is a dict whose values are either str or richer types, depending on the capabilities of each format

Exporting the configuration

The @export_config_option decorator adds a --export-config FORMAT option that resolves the CLI’s current configuration and writes it to <stdout> as a ready-to-use configuration file, then exits. It is part of the default options of every @command and @group, so click-extra CLIs ship with it out of the box.

The values are resolved through the usual precedence chain: command-line parameters override environment variables, which override an autodiscovered configuration file, which overrides the defaults. So combining --export-config with other options or environment variables captures them in the generated configuration, which makes it a convenient way to freeze the current invocation into a file or to produce a starting-point template.

from click_extra import command, echo, option

@command
@option("--city", default="Lisbon")
@option("--temperature", type=int, default=18)
@option("--tags", multiple=True, default=("sunny",))
def weather(city, temperature, tags):
    echo(f"{city}: {temperature}C {tags!r}")

A bare export renders every configurable parameter, including click-extra’s own built-in options:

$ weather --export-config toml
[weather]
city = "Lisbon"
temperature = 18
tags = ["sunny"]
time = false
accessible = false
color = "auto"
no-color = false
progress = true
theme = "dark"
table-format = "rounded-outline"
verbosity = "WARNING"
verbose = 0
quiet = 0
debug = false
tree = false
man = false
# help-format =

Any value set on the command line (or via an environment variable) is reflected in the export, so the output can be saved straight into a configuration file:

$ weather --city Oslo --temperature 4 --export-config toml
[weather]
city = "Oslo"
temperature = 4
tags = ["sunny"]
time = false
accessible = false
color = "auto"
no-color = false
progress = true
theme = "dark"
table-format = "rounded-outline"
verbosity = "WARNING"
verbose = 0
quiet = 0
debug = false
tree = false
man = false
# help-format =

Redirect the output to your configuration file to persist it:

$ weather --city Oslo --export-config toml > ~/.config/weather/config.toml

The accepted formats are the ones click-extra can serialize: toml, yaml, json, json5, jsonc, hjson, xml and plist. ini, sqlite, argfile and pyproject.toml have no serializer and cannot be exported. A format whose optional dependency is missing exits with code 1 and an install hint.

Exported keys use the canonical kebab-case spelling (see Key spelling), so the generated file reads like the CLI flags it mirrors.

Parameters without a value are exported too, so the generated file names every key a configuration file can set. Multi-value parameters read as empty lists, and unset scalars render as null, except in TOML which has no null type and comments them out:

from click_extra import command, echo, option

@command
@option("--regexp")
@option("--tags", multiple=True)
def filters(regexp, tags):
    echo(f"{regexp!r} {tags!r}")
$ filters --export-config toml
[filters]
# regexp =
tags = []
time = false
accessible = false
color = "auto"
no-color = false
progress = true
theme = "dark"
table-format = "rounded-outline"
verbosity = "WARNING"
verbose = 0
quiet = 0
debug = false
tree = false
man = false
# help-format =

If a configuration file is discovered or passed via --config, its values are loaded before the export renders, so the output reflects the full precedence chain regardless of the order of the flags on the command line. The same guarantee applies to --params.

Note

--export-config is itself excluded from the export, like the other introspection options (--help, --version, --params, --validate-config). It requires a sibling @config_option decorator to be present on the same command.

Excluding parameters

The excluded_params argument blocks listed CLI options from being loaded from configuration.

It defaults to the value of DEFAULT_EXCLUDED_PARAMS, plus the CLI’s --help option, resolved at runtime.

Set your own blocklist with the excluded_params argument:

from click import command, option, echo

from click_extra import config_option

@command
@option("--int-param", type=int, default=10)
@config_option(excluded_params=["my-cli.non_configurable_option", "my-cli.dangerous_param"])
def my_cli(int_param):
    echo(f"int_parameter is {int_param!r}")

Hint

Provide the fully-qualified ID of the option to block: the dot-separated ID prefixed by the CLI name. This reaches options at any level, including subcommands.

To discover options and their IDs, run your CLI with the --params option.

On the default @command and @group decorators, the excluded_params keyword extends the blocklist without replacing the whole default parameter list. Unlike the option-level argument above, it is additive: the built-in exclusions (--config, --version, --help, …) are preserved and your IDs are unioned into them.

from click_extra import command

@command(excluded_params=["my-cli.dangerous_param"])
def my_cli(): ...

Under strict mode, a blocked parameter found in a configuration file is refused with a dedicated message naming it as not allowed, rather than unknown.

Including parameters

The included_params argument is the inverse of excluded_params: only the listed parameters will be loaded from the configuration file. All other parameters found in the configuration will be ignored.

from click import command, option, echo

from click_extra import config_option

@command
@option("--flag-a/--no-flag-a")
@option("--flag-b/--no-flag-b")
@config_option(included_params=("my-cli.flag_a",))
def my_cli(flag_a, flag_b):
    echo(f"flag_a={flag_a!r}")
    echo(f"flag_b={flag_b!r}")

In the example above, only flag_a will be loaded from configuration. flag_b will keep its CLI default even if it is present in the configuration file.

Caution

included_params and excluded_params are mutually exclusive. Providing both will raise a ValueError.

Hint

Like excluded_params, this takes fully-qualified option IDs. Run your CLI with the --params option to discover them.

Schema-only configuration

When using config_schema for typed configuration access, your config keys typically don’t correspond to CLI parameters: they’re custom fields consumed via get_tool_config(). In that case, passing them through merge_default_map is unnecessary and can cause collisions if a config key happens to share a name with a subcommand.

Set included_params=() (empty tuple) to disable merge_default_map entirely. All configuration access goes through the schema:

from dataclasses import dataclass
from click_extra import group, pass_context
from click_extra.config import get_tool_config


@dataclass
class AppConfig:
    setup_guide: bool = True
    sync_interval: int = 60


@group(config_schema=AppConfig, schema_strict=True, included_params=())
@pass_context
def my_app(ctx):
    config = get_tool_config(ctx)
    # config is always an AppConfig instance, never None

Note

included_params=() is different from included_params=None. None means “not configured, use the default behavior” (which applies excluded_params). () means “the allowlist is explicitly empty: merge nothing into default_map.”

Disabling autodiscovery

By default, @config_option automatically searches for configuration files in the default application folder. If you want to disable this autodiscovery and only load a configuration file when the user explicitly passes --config <path>, use the NO_CONFIG sentinel as the default:

from click import group, option, echo
from click_extra import config_option, NO_CONFIG

@group(context_settings={"show_default": True})
@option("--dummy-flag/--no-flag")
@config_option(default=NO_CONFIG)
def my_cli(dummy_flag):
    echo(f"dummy_flag is {dummy_flag!r}")

With this setup:

  • The --help output shows [default: disabled] instead of a filesystem path.

  • Running the CLI without --config produces no configuration-related output on stderr.

  • Users can still explicitly pass --config <path> to load a specific configuration file.

  • The --no-config flag (if added via @no_config_option) still prints the “Skip configuration file loading altogether.” message when used explicitly.

This is useful for CLIs where configuration files are opt-in rather than opt-out, or when you want to avoid side effects from automatically discovered configuration files during development or testing.

Default subcommands

You can specify which subcommands run by default when a group is invoked without any explicit subcommands on the CLI. This is done via the _default_subcommands reserved configuration key.

Given this CLI:

from click_extra import echo, group, option


@group
def my_cli():
    pass


@my_cli.command()
@option("--path", default="/tmp")
def backup(path):
    echo(f"Backing up {path}")


@my_cli.command()
def sync():
    echo("Syncing")

And this TOML configuration:

[my-cli]
_default_subcommands = ["backup"]

[my-cli.backup]
path = "/home"

Running my-cli alone will automatically invoke the backup subcommand:

$ my-cli --config /tmp/tmpsc3nv2df/my-cli.toml
Load configuration matching /tmp/tmpsc3nv2df/my-cli.toml
Backing up /home

Chained commands

For groups created with chain=True, you can list multiple default subcommands. They run in the order specified. The rest of this section builds on a chained variant of the CLI above, with a debug subcommand to prepend later:

from click_extra import echo, group, option


@group(chain=True)
def my_cli():
    pass


@my_cli.command()
@option("--path", default="/tmp")
def backup(path):
    echo(f"Backing up {path}")


@my_cli.command()
def sync():
    echo("Syncing")


@my_cli.command()
def debug():
    echo("Debug mode activated")
[my-cli]
_default_subcommands = ["backup", "sync"]
$ my-cli --config /tmp/tmp_iqzgfgq/my-cli.toml
Load configuration matching /tmp/tmp_iqzgfgq/my-cli.toml
Backing up /home
Syncing

Note

Non-chained groups only accept a single default subcommand. Listing more than one will produce an error.

CLI precedence

If the user names subcommands explicitly on the command line, the _default_subcommands configuration is ignored:

$ my-cli --config /tmp/tmpe7iyhce7/my-cli.toml sync
Load configuration matching /tmp/tmpe7iyhce7/my-cli.toml
Syncing

Prepend subcommands

The _prepend_subcommands key always prepends subcommands to every invocation, regardless of whether CLI subcommands are provided. This is useful for always injecting a subcommand (like debug) on a dev machine.

Important

_prepend_subcommands only works with chain=True groups. Non-chained groups resolve exactly one subcommand, so prepending would break the user’s intended command.

[my-cli]
_prepend_subcommands = ["debug"]

Running my-cli sync effectively becomes my-cli debug sync:

$ my-cli --config /tmp/tmpyr5_ldjd/my-cli.toml sync
Load configuration matching /tmp/tmpyr5_ldjd/my-cli.toml
Debug mode activated
Syncing

_default_subcommands with _prepend_subcommands

When both keys are set and no CLI subcommands are given, _default_subcommands fires first, then _prepend_subcommands is prepended. The result is [*prepend, *defaults]:

[my-cli]
_default_subcommands = ["sync"]
_prepend_subcommands = ["debug"]
$ my-cli --config /tmp/tmpvdl1_0r6/my-cli.toml
Load configuration matching /tmp/tmpvdl1_0r6/my-cli.toml
Debug mode activated
Syncing

When CLI subcommands are given explicitly, _default_subcommands is ignored but _prepend_subcommands still applies:

$ my-cli --config /tmp/tmpm02ljq7s/my-cli.toml backup
Load configuration matching /tmp/tmpm02ljq7s/my-cli.toml
Debug mode activated
Backing up /tmp

Fallback sections

When a CLI tool is renamed, existing configuration files may still use the old section name. The fallback_sections parameter lets you accept legacy names with a deprecation warning:

from dataclasses import dataclass
from click_extra import echo, group, pass_context
from click_extra.config import get_tool_config

@dataclass
class ToolConfig:
    value: str = "default"

@group(
    config_schema=ToolConfig,
    fallback_sections=("old-tool-name", "even-older-name"),
)
@pass_context
def new_tool(ctx):
    """A tool that was renamed."""
    config = get_tool_config(ctx)
    if config is not None:
        echo(f"value: {config.value}")

@new_tool.command()
def run():
    """Run the tool."""
    echo("done")

With the following TOML:

Legacy configuration still using the old name.
[old-tool-name]
value = "from-legacy"

The CLI loads the [old-tool-name] section and logs a deprecation warning to stderr:

Config section [old-tool-name] is deprecated, migrate to [new-tool].

If both [new-tool] and [old-tool-name] exist, the current name always wins, and a warning is emitted about the leftover legacy section.

$ new-tool --help
Usage: new-tool [OPTIONS] COMMAND [ARGS]...

  A tool that was renamed.

Options:
  -h, --help                   Show this message and exit.

Configuration options:
  --config LOCATION            Location of the configuration file. Supports
                               local path with glob patterns or remote URL.
                               [default: ~/.config/new-tool/]
  --no-config                  Ignore all configuration files and only use
                               command line parameters and environment
                               variables.
  --validate-config LOCATION   Validate the configuration file and exit.
  --export-config FORMAT       Export the configuration in the selected format
                               to <stdout>, then exit.

Output options:
  --accessible                 Accessibility mode: disable colors and render
                               tables in a borderless, screen-reader-friendly
                               format.
  --color [auto|always|never]  Colorize the output. A bare --color is the same
                               as --color=always.  [default: auto]
  --no-color                   Disable colorization (alias of --color=never).
  --progress / --no-progress   Show progress indicators during long operations.
                               Disabled for non-interactive output (pipes, dumb
                               terminals, CI) and by --accessible.  [default:
                               progress]
  --theme [auto|dark|dracula|light|manpage|monokai|nord|solarized-dark]
                               Color theme used for help screens.  [default:
                               dark]
  --table-format FORMAT        Rendering style of tables.  [default: rounded-
                               outline]

Logging options:
  --verbosity LEVEL            Either CRITICAL, ERROR, WARNING, INFO, DEBUG.
                               [default: WARNING]
  -v, --verbose                Increase the default WARNING verbosity by one
                               level for each additional repetition of the
                               option.  [default: 0]
  -q, --quiet                  Decrease the default WARNING verbosity by one
                               level for each additional repetition of the
                               option.  [default: 0]
  --debug                      Shorthand for --verbosity DEBUG.

Introspection options:
  --time / --no-time           Measure and print elapsed execution time.
                               [default: no-time]
  --params                     Show all CLI parameters, their provenance,
                               defaults and value, then exit.
  --tree                       Show the tree of nested subcommands and exit.
  --man                        Read the command's manual page and exit.
  --help-format [carapace|json|json-full|man|markdown|markdown-full]
                               Render the command in the given format and exit.
  --version                    Show the version and exit.

Commands:
  help  Show help for a command.
  run   Run the tool.

This works identically across all configuration formats (TOML, YAML, JSON, INI, etc.), since the section lookup operates on the normalized dict structure after parsing.

click_extra.config API

Click Extra’s own built-in configuration schema and validators.

These are concrete instances of the configuration machinery, kept apart from the reusable engine in schema and the option classes in option:

  • the dataclasses describing click-extra’s own [tool.click-extra] section (ClickExtraConfig and its test-suite and prebake sub-tables);

  • _builtin_config_validators(), the validators click-extra registers on every ConfigOption.

A downstream project defines its own equivalent of this module; click-extra just happens to ship one, built on the same generic engine.

click_extra.config.builtin.THEMES_CONFIG_KEY: str = 'themes'

Sub-key under [tool.<cli>] where user-defined themes live in config.

Used by ConfigOption to find [tool.<cli>.themes.<name>] tables, build them via HelpTheme.from_dict, and stash the result on ctx.meta[click_extra.context.THEME_OVERRIDES]. The constant is the single source of truth shared by _builtin_config_validators, ConfigOption._apply_theme_overrides, and click_extra.theme.themes_from_config().

class click_extra.config.builtin.TestSuiteConfig(file='./tests/cli-test-suite.toml', cases=<factory>, timeout=None)[source]

Bases: object

Config schema for a project’s test suite, read from [tool.<cli>.test-suite].

The test-suite CLI command resolves its cases from this config when no suite is given on the command line. Map it onto an app’s config section with a field carrying metadata={CONFIG_PATH_METADATA_KEY: "test-suite"}.

file: str = './tests/cli-test-suite.toml'

Path to a test suite file, resolved relative to the project root.

Its format is detected from the extension; the default is TOML, which (like JSON) parses with no optional dependency, unlike YAML and the others.

cases: list[dict]

Test cases written natively in the config format, an alternative to a file suite, taking precedence over it when both are set.

Each entry is a mapping of CLITestCase directive names, equivalent to one item of a suite list. In TOML this reads as a [[tool.<cli>.test-suite.cases]] array of tables. Declared as an extension point so the configuration engine passes the raw mappings through unprocessed; the test-suite command turns them into CLITestCase instances.

timeout: int | None = None

Default timeout (seconds) for each case that does not set its own.

None leaves cases unbounded unless --timeout is passed.

class click_extra.config.builtin.PrebakeConfig(module=None)[source]

Bases: object

Config schema for the prebake commands, read from [tool.<cli>.prebake].

Lets a project pin the target __init__.py once for its build pipeline, instead of passing --module to every click-extra prebake command.

module: str | None = None

Path to the __init__.py to pre-bake, resolved relative to the project root. Overrides the [project.scripts] auto-discovery; leave unset to keep it.

class click_extra.config.builtin.ClickExtraConfig(test_suite=<factory>, prebake=<factory>)[source]

Bases: object

Schema for the [tool.click-extra] configuration section.

Wired as the config_schema of the top-level click-extra group, so every subcommand reads the same section and pulls its own sub-table through get_tool_config().

test_suite: TestSuiteConfig

The [tool.click-extra.test-suite] sub-table (file/cases/timeout).

prebake: PrebakeConfig

The [tool.click-extra.prebake] sub-table (target module).

        classDiagram
  Enum <|-- Sentinel
  ExtraOption <|-- ConfigOption
  ExtraOption <|-- ExportConfigOption
  ExtraOption <|-- NoConfigOption
  ExtraOption <|-- ValidateConfigOption
  ParamStructure <|-- ConfigOption
    

Utilities to load parameters and options from a configuration file.

Hint

Why config?

That whole namespace is using the common config short-name to designate configuration files.

Not conf, not cfg, not configuration, not settings. Just config.

A quick survey of existing practices, and poll to my friends informed me that config is more explicit and less likely to be misunderstood.

After all, is there a chance for it to be misunderstood, in the context of a CLI, for something else? Confirm? Conference? Conflict Confuse?…

So yes, config is good enough.

Dotted keys in configuration files (like "subcommand.option": value) are automatically expanded into nested dicts before merging, so users can freely mix flat dot-notation and nested structures in any supported format.

click_extra.config.option.get_app_dir(app_name, roaming=True, force_posix=False)

click.get_app_dir()’s Unix branch, taken on every platform.

Returns an expanded path like the original, since a caller resolves it before Click Extra shrinks the home prefix back to ~ for display. XDG_CONFIG_HOME is deliberately ignored: honoring it would put the host back into the rendered output.

click_extra.config.option.VCS_DIRS = ('.git', '.hg', '.svn', '.bzr', 'CVS', '.darcs')

VCS directory names used to identify version control system roots.

Includes: - .git: Git - .hg: Mercurial - .svn: Subversion - .bzr: Bazaar - CVS: CVS (note: uppercase, no leading dot) - .darcs: Darcs

click_extra.config.option.CONFIG_OPTION_NAME = 'config'

Hardcoded name of the configuration option.

This name is going to be shared by both the --config and --no-config options below, so they can compete with each other to either set a path pattern or disable the use of any configuration file at all.

click_extra.config.option.DEFAULT_EXCLUDED_PARAMS = ('config', 'export_config', 'params', 'validate_config', 'version')

Default parameter IDs to exclude from the configuration file.

Defaults to:

  • --config option, which cannot be used to recursively load another configuration file.

  • --export-config flag, which like --params introspects the CLI and exits, so it has no place in the configuration it would export.

  • --params flag, which is like --help and stops the CLI execution.

  • --validate-config option, which belongs to the same self-referential config machinery as --config and --export-config.

  • --version, which is not a configurable option per-se.

--help is excluded too (it makes no sense to have a configuration file always force a CLI to show the help and exit), but is deliberately absent from this tuple: unlike the entries above, click-extra does not control that option’s internal name. It is resolved at runtime instead, in ConfigOption.excluded_params, so a rename on Click’s own side does not silently stop excluding it.

class click_extra.config.option.Sentinel(*values)[source]

Bases: Enum

Enum used to define sentinel values.

Note

This reuse the same pattern as Click._utils.Sentinel.

NO_CONFIG = <object object>
VCS = <object object>
click_extra.config.option.NO_CONFIG = Sentinel.NO_CONFIG

Sentinel used to indicate that no configuration file must be used at all.

click_extra.config.option.VCS = Sentinel.VCS

Sentinel used to stop parent directory walking at the nearest VCS root.

class click_extra.config.option.ConfigOption(param_decls=None, metavar='LOCATION', type=UNPROCESSED, help='Location of the configuration file. Supports local path with glob patterns or remote URL.', is_eager=True, expose_value=False, file_format_patterns=None, file_pattern_flags=4104, show_file_patterns=None, roaming=True, force_posix=False, search_pattern_flags=285504, search_parents=False, stop_at=Sentinel.VCS, cascade=False, excluded_params=None, included_params=None, strict=False, config_schema=None, schema_strict=False, fallback_sections=(), config_validators=(), **kwargs)[source]

Bases: ExtraOption, ParamStructure

A pre-configured option adding --config LOCATION.

Takes as input a path to a file or folder, a glob pattern, or an URL.

  • is_eager is active by default so the callback gets the opportunity to set the default_map of the CLI before any other parameter is processed.

  • default is set to the value returned by self.default_pattern(), which is a pattern combining the default configuration folder for the CLI (as returned by click.get_app_dir()) and all supported file formats.

    Attention

    Default search pattern must follow the syntax of wcmatch.glob.

  • excluded_params are parameters which, if present in the configuration file, will be ignored and not applied to the CLI. Items are expected to be the fully-qualified ID of the parameter, as produced in the output of --params. Will default to the value of DEFAULT_EXCLUDED_PARAMS, plus the CLI’s --help option, resolved at runtime.

  • included_params is the inverse of excluded_params: only the listed parameters will be loaded from the configuration file. Cannot be used together with excluded_params.

file_format_patterns: dict[ConfigFormat, tuple[str, ...]]

Mapping of ConfigFormat to their associated file patterns.

Can be a string or a sequence of strings. This defines which configuration file formats are supported, and which file patterns are used to search for them.

Note

All formats depending on third-party dependencies that are not installed will be ignored.

Attention

File patterns must follow the syntax of wcmatch.fnmatch.

auto_file_formats

Whether the format set was inherited instead of chosen by the developer.

True when no file_format_patterns was provided, so the set is whatever the installed extra dependencies enable. It ranges from 3 patterns on a bare install to 14 with every extra, which is an artifact of the environment rather than a decision the CLI made.

Only collapse_default() reads it, and only when show_file_patterns is left at None.

file_pattern_flags

Flags provided to all calls of wcmatch.fnmatch.

Applies to the matching of file names against supported format patterns specified in file_format_patterns.

Important

The SPLIT flag is always forced, as our multi-pattern design relies on it.

show_file_patterns

Whether the help screen prints the file patterns of the default.

Follows the tri-state convention of Click Extra’s other display settings:

  • None prints them when the developer chose the format set, and hides them when it was inherited from the install.

  • True always prints them, which is how a CLI advertises the formats its own dependencies enable.

  • False always hides them.

See collapse_default() for what each state renders.

force_posix

Configuration for default folder search.

roaming and force_posix are fed to click.get_app_dir() to determine the location of the default configuration folder.

search_pattern_flags

Flags provided to all calls of wcmatch.glob.

Applies to both the default pattern and any user-provided pattern.

Important

The BRACE flag is always forced, so that multi-format default patterns using {pat1,pat2,...} syntax expand correctly.

The NODIR flag is always forced, to optimize the search for files only.

search_parents

Indicates whether to walk back the tree of parent folders when searching for configuration files.

stop_at

Boundary for parent directory walking.

  • None: walk up to filesystem root.

  • VCS: stop at the nearest VCS root, whichever system marks it (see VCS_DIRS) (default).

  • A Path or str: stop at that directory.

cascade

Merge every discovered configuration file instead of stopping at the first parseable one.

When True, all files found by auto-discovery (the app-dir search, including the parent walk when search_parents=True, plus the pyproject.toml CWD search when enabled) are loaded and layered into the context’s default_map via a ~collections.ChainMap. The most local file wins on key lookup: a pyproject.toml found near the CWD overrides the app-dir config, which overrides files found higher up the parent walk.

An explicit --config value never cascades: it pins a single source, whatever the pattern matches.

Defaults to False, which preserves the historical behavior of the first successfully parsed file winning.

extra_excluded_params: frozenset[str]

Additional exclusions merged into the dynamic excluded_params default.

Populated by Command’s excluded_params forwarding, which is additive: the default blocklist (--config, --version, --help, …) is preserved and the forwarded IDs are unioned into it when the property resolves. Ignored when an explicit excluded_params was frozen on the instance, as the property is then never consulted.

included_params: frozenset[str] | None

Allowlist of parameter IDs, mutually exclusive with excluded_params.

None disables the allowlist. It is resolved into excluded_params by build_param_trees(), once every parameter ID is known.

strict

Defines the strictness of the configuration loading.

  • If True, raise an error if the configuration file contain parameters not recognized by the CLI.

  • If False, silently ignore unrecognized parameters.

config_schema

Optional schema for structured access to configuration values.

When set, the app’s configuration section is extracted from the parsed config file, normalized (hyphens replaced with underscores), flattened (nested dicts joined with _), and passed to this callable to produce a typed configuration object.

Supports:

  • Dataclass types: detected via __dataclass_fields__. Keys are normalized, nested dicts are flattened, and the result is filtered to known fields before instantiation. This allows nested config sections (like [tool.myapp.sub-section]) to map directly to flat dataclass fields (like sub_section_key).

  • Any callable dict T: called directly with the raw dict. Works with Pydantic’s Model.model_validate, attrs, or custom factory functions. The caller is responsible for key normalization and flattening.

The resulting object is stored in ctx.meta[click_extra.context.TOOL_CONFIG] and can be retrieved via get_tool_config.

schema_strict

Strictness for schema validation (separate from strict).

  • If True, raise ValueError when the config section contains keys that do not match any dataclass field (after normalization and flattening). Only applies when config_schema is a dataclass.

  • If False, ignore unrecognized keys. When the section is schema-only (included_params=()), a warning still names them: see warn_unknown in make_schema_callable().

Note

This is distinct from strict, which controls whether merge_default_map rejects config keys not matching CLI parameters. schema_strict validates against dataclass fields instead.

fallback_sections: Sequence[str]

Legacy section names to try when the app’s own section is empty.

Useful when a CLI tool has been renamed: old configuration files that still use [tool.old-name] (TOML), old-name: (YAML), or {"old-name": …} (JSON) are recognized with a deprecation warning. Works with all configuration formats.

schema_warn_unknown: bool

Warn on config keys unknown to the schema, in lax mode.

Inferred, not user-supplied: an explicitly empty included_params means no CLI parameter is merged from the app’s config section, so the section is schema-only and any key the schema does not know is a typo worth a warning. Forwarded to make_schema_callable() and the validation pipeline as warn_unknown.

config_validators: tuple[ConfigValidator, ...]

Extension validators for sub-trees of the configuration file.

Each ConfigValidator targets a dotted extension_path relative to the app section. Validators run after click-extra’s built-in CLI-parameter strict check (during --validate-config) and after the schema callable produces the typed configuration object (during normal config loading).

The list is seeded with click-extra’s built-in validators (currently the one for [tool.<cli>.themes.<name>] tables, see click_extra.theme.validate_themes_config()); user-supplied validators are appended after them. App code that registers its own validator on the same extension_path simply runs alongside the built-in: both validators are called, both sets of errors surface.

property excluded_params: frozenset[str][source]

Generates the default list of fully-qualified IDs to exclude.

Danger

It is only called once to produce the default exclusion list if the user did not provided its own.

It was not implemented in the constructor but made as a property, to allow for a just-in-time call within the current context. Without this trick we could not have fetched the CLI name.

property file_pattern: str[source]

Compile all file patterns from the supported formats.

Uses , (comma) notation to combine multiple patterns, suitable for wcmatch brace expansion ({pat1,pat2,...}).

Returns a single pattern string.

default_pattern()[source]

Returns the default pattern used to search for the configuration file.

Defaults to <app_dir>/{*.toml,*.json,*.ini}. Where <app_dir> is produced by the click.get_app_dir() method. The result depends on OS and is influenced by the roaming and force_posix properties.

Multiple file format patterns are wrapped with {…} brace-expansion syntax so that wcmatch.glob correctly applies the directory prefix to every sub-pattern.

Note

A CLI wanting another folder layout, like the one platformdirs computes, passes its own pattern to default instead. That keeps the layout a choice of the CLI rather than a dependency of this package: see the documentation.

Return type:

str

get_help_extra(ctx)[source]

Replaces the default value of the configuration option.

Display a pretty path that is relative to the user’s home directory:

~/folder/my_cli/{*.toml,*.json,*.ini}

Instead of the full absolute path:

/home/user/folder/my_cli/{*.toml,*.json,*.ini}
Return type:

OptionHelpExtra

Caution

This only applies when the GLOBTILDE flag is set in search_pattern_flags.

An inherited format set is then reduced to the folder it searches, as described in collapse_default().

render_default(ctx)[source]

The default search pattern, as a portable home-relative path.

Keeps the whole pattern, file patterns included. The help screen collapses an inherited set on top of this with collapse_default(), but a consumer with room for the files (the FILES section of a man page) calls this method instead.

Return type:

str

collapse_default(default)[source]

Reduce an inherited default pattern to the folder it searches.

A CLI installed with every extra searches 15 file patterns, so its default renders as a 136-character glob. The help screen has no space for it and no place to break it, so Click splits it mid-word:

[default: ~/.config/hello/{*.
toml,*.yaml,*.yml,*.json,*.json5,*.jwcc,*.jsonc,
*.hjson,*.ini,*.xml,*.plist,*.sqlite,*.sqlite3,*
.conf,pyproject.toml}]

Rendering the folder alone answers the question a reader opens --help for, on one line, and keeps the answer the same on every install:

[default: ~/.config/hello/]

A developer who passed file_format_patterns chose that set, so it is displayed in full: the pattern is short enough to read, and the help screen is where the choice shows up. show_file_patterns overrides that reading in either direction, and True is what a CLI advertising its formats wants: the set is computed from the installed dependencies at each invocation, so the help screen reports what that install can really parse. The complete pattern of any CLI stays available in the output of --params.

The trailing separator marks the value as a folder, since it is a search base and not a location the option accepts back.

Return type:

str

parent_patterns(pattern)[source]

Generate (root_dir, file_pattern) pairs for searching.

Each yielded pair can be passed directly to glob.iglob(file_pattern, root_dir=root_dir) so that every sub-pattern (whether from BRACE or SPLIT expansion) is correctly scoped to the same directory.

root_dir is None for entirely magic patterns that will be evaluated relative to the current working directory.

Stops when reaching the root folder, the stop_at boundary, or an inaccessible directory.

Return type:

Iterable[tuple[str | None, str]]

search_and_read_file(pattern)[source]

Search filesystem or URL for files matching the pattern.

If pattern is an URL, download its content. A pattern is considered an URL only if it validates as one and starts with http:// or https://. All other patterns are considered glob patterns for local filesystem search.

Returns an iterator of (location, content, media_type) triples, for each one matching the pattern. location is normalized and content raw. media_type is the bare type/subtype the server advertised in its Content-Type header, and is None for a local file, whose format is derived from its name. Only files are returned, directories are silently skipped.

This method returns the raw content of all matching patterns, without trying to parse them. If the content is empty, it is still returned as-is.

Also includes lookups into parents directories if self.search_parents is True.

Raises FileNotFoundError if no file was found after searching all locations.

Return type:

Iterable[tuple[Path | URL, str, str | None]]

parse_conf(content, formats, location=None)[source]

Parse the content with the given formats.

Tries to parse the given raw content string with each of the given formats, in order. Yields the resulting data structure for each successful parse.

location is the path the content was read from. It is only needed by formats that cannot be parsed from a text payload, like SQLITE, which is read straight from its file, and the binary variant of PLIST, which only exists on disk. Such formats are skipped when location is missing or is not a local file.

Attention

Formats whose parsing raises an exception or does not return a dict are considered a failure and are skipped.

This follows the parse, don’t validate principle.

Return type:

Iterable[dict[str, Any] | None]

read_and_parse_all_conf(pattern)[source]

Search for every parseable configuration file matching pattern.

Yields (location, parsed_conf) pairs in discovery order, which is the most local first: the original search location, then each parent directory when parent search is enabled. Files already yielded (as matched by their resolved location) are skipped, as are files that parse to an empty configuration.

Raises FileNotFoundError if no file at all matched the pattern.

Return type:

Iterable[tuple[Path | URL, dict[str, Any]]]

read_and_parse_conf(pattern)[source]

Search for a parseable configuration file.

Returns the location and data structure of the first configuration matching the pattern.

Only return the first match that:

  • exists,

  • is a file,

  • is not empty,

  • match file format patterns,

  • can be parsed successfully, and

  • produce a non-empty data structure.

Raises FileNotFoundError if no configuration file was found matching the criteria above.

Returns (None, None) if files were found but none could be parsed.

Return type:

tuple[Path | URL, dict[str, Any]] | tuple[None, None]

load_ini_config(content)[source]

Utility method to parse INI configuration file.

Internal convention is to use a dot (., as set by PARAM_PATH_SEP) in section IDs as a separator between levels. This is a workaround the limitation of INI format which doesn’t allow for sub-sections.

Returns a ready-to-use data structure.

Return type:

dict[str, Any]

load_argfile_config(content)[source]

Utility method to parse a plain-text argfile configuration file.

The file holds command-line tokens, one option per line, in the style of mpv’s and yt-dlp’s configuration files:

# Comments start with a hash sign.
--option-name some value
--flag

Tokens are split with shlex.split(), so shell quoting rules apply and a # starts a comment. Each option is matched against the CLI’s root-level parameter declarations, and its value is converted to the parameter’s Python type, like load_ini_config() does. A boolean flag needs no value: its primary declaration sets it to True, its secondary one (--no-*) to False. An option flagged multiple accumulates one list item per occurrence. Unknown options are kept under a normalized key so the strict check can reject them like any other unrecognized configuration key, while positional tokens are skipped; subcommand options cannot be addressed from an argfile.

Returns a ready-to-use data structure, wrapped in the app’s section name like the [my-cli] section of the other formats.

Raises:

ValueError – the content cannot be tokenized, or an option is missing its value.

Return type:

dict[str, Any]

load_sqlite_config(path)[source]

Utility method to parse a SQLite configuration database.

The database holds a single SQLITE_CONFIG_TABLE table of key/value rows. Keys are parameter paths, with a dot (., as set by PARAM_PATH_SEP) separating each level, like my-cli.default.int_param. Values are JSON-encoded, which carries every type the other formats do: booleans, numbers, strings, lists and nested objects alike.

Returns a ready-to-use data structure.

Note

sqlite3 is imported here and not at the top of the module, like the optional parsers of parse_content(). A distribution can ship a Python without the SQLite bindings, and an unconditional import would then break every CLI at import time. SQLITE_SUPPORT reports whether they are there, and disables the format if they are not.

Return type:

dict[str, Any]

load_plist_config(path)[source]

Utility method to parse a plist configuration file.

The file is read as raw bytes and handed to the standard library’s plistlib, which transparently decodes both the XML and the binary variants of the format. The XML variant also parses from a text payload through parse_content(), which is how a plist fetched over http:// or https:// is loaded.

Returns a ready-to-use data structure.

Return type:

dict[str, Any]

merge_default_map(ctx, user_conf)[source]

Save the user configuration into the context’s default_map.

Merge the user configuration into the pre-computed template structure, which filters out all unrecognized options not supported by the command, then hand the result to _install_default_map().

Opaque sub-trees declared by the schema or by registered ConfigValidator instances are stripped from the conf before the CLI-parameter strict check, so user-controlled keys (like mappings whose keys are data, not flag names) don’t trip strict=True.

Note

This recomputes the filtered config that run_config_validation() already produces as merged_conf. load_conf() installs that result directly and skips this method; it stays as the standalone entry point for external callers.

Return type:

None

load_conf(ctx, param, path_pattern)[source]

Fetch parameter values from a configuration file and set them as defaults.

User configuration is merged to the context’s default_map, like Click does.

By relying on Click’s default_map, we make sure that precedence is respected. Direct CLI parameters, environment variables or interactive prompts take precedence over any values from the config file.

Hint

Once loading is complete, the resolved file path and its full parsed content are stored in ctx.meta[click_extra.context.CONF_SOURCE] and ctx.meta[click_extra.context.CONF_FULL] respectively. This is the recommended way to identify which configuration file was loaded.

We intentionally do not add a custom ParameterSource.CONFIG_FILE enum member: ParameterSource is a closed enum in Click, and monkeypatching it would be fragile. Besides, config values end up in default_map, so Click already reports them as ParameterSource.DEFAULT_MAP, which is accurate.

Return type:

None

class click_extra.config.option.NoConfigOption(param_decls=None, help='Ignore all configuration files and only use command line parameters and environment variables.', is_flag=True, flag_value=Sentinel.NO_CONFIG, is_eager=True, expose_value=False, **kwargs)[source]

Bases: ExtraOption

A pre-configured option adding --no-config.

This option is supposed to be used alongside the --config option (ConfigOption) to allow users to explicitly disable the use of any configuration file.

This is especially useful to debug side-effects caused by autodetection of configuration files.

flag_value=NO_CONFIG is the Sentinel enum member that signals “skip configuration loading” to ConfigOption. Click 8.4.0 (PR pallets/click#3363) auto-detects type=UNPROCESSED for non-basic flag_value types, so the sentinel passes through Option unchanged without an explicit type override.

See also

An alternative implementation of this class would be to create a custom click.ParamType instead of a custom Option subclass. Here is for example.

check_sibling_config_option(ctx, param, value)[source]

Ensure that this option is used alongside a ConfigOption instance.

Return type:

None

class click_extra.config.option.ValidateConfigOption(param_decls=None, type=UNPROCESSED, metavar='LOCATION', is_eager=True, expose_value=False, help='Validate the configuration file and exit.', **kwargs)[source]

Bases: ExtraOption

A pre-configured option adding --validate-config LOCATION.

Loads the config file at the given location, validates it against the CLI’s parameter structure in strict mode, reports results, and exits.

Note

The value is left UNPROCESSED so it accepts everything ConfigOption accepts: a file, a folder, a glob pattern, or an http:// or https:// URL. Both options hand their value to the same ConfigOption.read_and_parse_conf(), so a configuration a CLI can load is a configuration it can also validate.

validate_config(ctx, param, value)[source]

Load, parse, and validate the configuration file, then exit.

Validation runs three checks in order, every one of them under the same ValidationError shape so the reported path is always rooted at the configuration file:

  1. CLI-parameter strict check on the non-opaque part of the document.

  2. Schema processing, if a config_schema is configured: catches type errors and unknown keys inside the dataclass-described section.

  3. Each registered ConfigValidator runs against its declared opaque sub-tree.

Every detected error is emitted before exiting, so a single --validate-config run surfaces the full list of fixes the user needs to apply.

Return type:

None

click_extra.config.option.ensure_config_loaded(ctx)[source]

Run the sibling ConfigOption’s resolution if it has not run yet.

Click processes eager parameters given on the command line before eager parameters left at their defaults, so an explicitly-passed introspection flag (--params, --export-config) fires before the --config option had a chance to discover and load the configuration file. The views those flags render would then miss the configuration layer entirely, showing defaults where the user’s config applies.

Idempotent: does nothing when the config option already resolved (its callback stamps CONF_SOURCE on the context even when no file was found), when the command has no config option, or when the option carries no callback.

Return type:

None

class click_extra.config.option.ExportConfigOption(param_decls=None, type=None, metavar='FORMAT', is_eager=True, expose_value=False, help='Export the configuration in the selected format to <stdout>, then exit.', **kwargs)[source]

Bases: ExtraOption

A pre-configured option adding --export-config FORMAT.

Resolves the CLI’s current parameter values following Click’s precedence chain (command line, then environment variables, then configuration file, then defaults), renders them as a configuration file in the requested format on <stdout>, and exits.

Hint

Combine the flag with other options or environment variables to capture them in the generated configuration. For example, mycli --verbosity DEBUG --export-config toml``emits a configuration whose``verbosity is already set to DEBUG.

Like ValidateConfigOption, it relies on a sibling ConfigOption to provide the parameter structure and the excluded_params / included_params filter, so the export contains exactly the parameters that can be loaded back from a configuration file.

Note

The accepted formats are those serialize_content() can write (SERIALIZABLE_FORMATS). INI, Argfile and pyproject.toml have no serializer and cannot be dumped.

build_config(ctx, config_option)[source]

Resolve every config-eligible parameter into a dumpable tree.

Walks the sibling ConfigOption’s parameter structure, resolves each parameter’s effective value by replaying RAW_ARGS (falling back to defaults when the command did not capture them), drops the excluded_params, and layers the coerced values into the {cli-name: {param: value, ...}} shape a configuration file uses. Parameter keys are rendered in their kebab-case spelling, the canonical presentation for configuration files; either spelling loads back to the same parameter.

Parameters without a default are kept as None leaves so the export names every key a configuration file can set: serializers render them as null, except TOML which comments them out (see _serialize_toml_with_unset()). Loading null back is harmless: ConfigOption._install_default_map() cleans blank values out of the merged result.

Return type:

dict[str, Any]

export_config(ctx, param, value)[source]

Render the resolved configuration to <stdout> and exit.

Return type:

None