Skip to content

Config templates

Scaffold a TOML config file from the [config: key] annotations in a usage message - the file the docopt(config=...) mapping is loaded from. See the config skeleton guide.

generate_config_template

generate_config_template(doc: str) -> str

Generate a TOML config-file skeleton from the [config: key] annotations in doc.

Every option declaring a [config: dotted.key] becomes an entry under its table, seeded with the option's [default: ...] (or an empty placeholder), and commented with the CLI flag and any [env: VAR] it also reads. Options without a [config:] key are not part of the file. Returns an empty string when the usage declares no config keys. Config keys that cannot coexist in one TOML document (a duplicate, or a path that is a prefix of another) raise DocoptLanguageError.

Source code in src/docopt2/_generate.py
def generate_config_template(doc: str) -> str:
    """Generate a TOML config-file skeleton from the ``[config: key]`` annotations in ``doc``.

    Every option declaring a ``[config: dotted.key]`` becomes an entry under its table, seeded with the
    option's ``[default: ...]`` (or an empty placeholder), and commented with the CLI flag and any
    ``[env: VAR]`` it also reads. Options without a ``[config:]`` key are not part of the file. Returns
    an empty string when the usage declares no config keys. Config keys that cannot coexist in one TOML
    document (a duplicate, or a path that is a prefix of another) raise
    [`DocoptLanguageError`][docopt2.DocoptLanguageError].
    """
    single_usage_section(doc)  # fail loudly on a malformed usage, like the other generators
    config_options = [option for option in parse_defaults(doc) if option.config_key is not None]
    _reject_colliding_config_keys([str(option.config_key) for option in config_options])
    tables: dict[str, list[tuple[str, Option]]] = {}
    order: list[str] = []
    for option in config_options:
        *prefix, leaf = str(option.config_key).split(".")
        table = ".".join(prefix)
        if table not in tables:
            tables[table] = []
            order.append(table)
        tables[table].append((leaf, option))
    # TOML requires root keys before any [table] header, so emit the unnamed table first.
    order = ([""] if "" in tables else []) + [table for table in order if table]
    blocks: list[str] = []
    for table in order:
        header = ["[" + ".".join(_toml_key(segment) for segment in table.split(".")) + "]"] if table else []
        rows = [(f"{_toml_key(leaf)} = {_toml_value(opt.value)}", _config_comment(opt)) for leaf, opt in tables[table]]
        width = max(len(entry) for entry, _ in rows)
        blocks.append("\n".join([*header, *(f"{entry.ljust(width)}{comment}" for entry, comment in rows)]))
    return "\n\n".join(blocks) + "\n" if blocks else ""