# Config Files

For more complicated CLI applications, it is common to have an external user configuration file. For example, the popular python tools `poetry`, `ruff`, and `pytest` are all configurable from a `pyproject.toml` file. The `App.config` attribute accepts a callable (or list of callables) that add (or remove) values to the parsed CLI tokens. The provided callable must have signature:

    def config(apps: List["App"], commands: Tuple[str, ...], arguments: ArgumentCollection):
        """Modifies the argument collection inplace with some injected values.

        Parameters
        ----------
        apps: Tuple[App, ...]
           The application hierarchy that led to the current command function.
           The current command app is the last element of this tuple.
        commands: Tuple[str, ...]
           The CLI strings that led to the current command function.
        arguments: ArgumentCollection
           Complete ArgumentCollection for the app.
           Modify this collection inplace to influence values provided to the function.
        """
        ...

The provided `config` does not have to be a function; all the Cyclopts builtin configs are classes that implement the `__call__` method. The Cyclopts builtins offer good standard functionality for common configuration files like yaml or toml.

## TOML Example

In this example, we create a small CLI tool that counts the number of times a given character occurs in a file.

    # character-counter.py
    import cyclopts
    from pathlib import Path

    app = cyclopts.App(
        name="character-counter",
        config=cyclopts.config.Toml(
            "pyproject.toml",  # Name of the TOML File
            root_keys=["tool", "character-counter"],  # The project's namespace in the TOML.
            # If "pyproject.toml" is not found in the current directory,
            # then iteratively search parenting directories until found.
            search_parents=True,
        ),
    )

    @app.command
    def count(filename: Path, *, character="-"):
        print(filename.read_text().count(character))

    app()

Running this code without a `pyproject.toml` present:

    $ python character-counter.py count README.md
    70
    $ python character-counter.py count README.md --character=t
    380

We can have the new default character be `t` by adding the following to `pyproject.toml`:

    [tool.character-counter.count]
    character = "t"

Rerunning the app without a specified `--character` will result in using the toml-provided value:

    $ python character-counter.py count README.md
    380

## Environment Variable Example

To automatically derive and read appropriate environment variables, use the `cyclopts.config.Env` class. Continuing the above TOML example:

    # character-counter.py
    import cyclopts
    from pathlib import Path

    app = cyclopts.App(
        name="character-counter",
        config=cyclopts.config.Env(
            "CHAR_COUNTER_",  # Every environment variable will begin with this.
        ),
    )

    @app.command
    def count(filename: Path, *, character="-"):
        print(filename.read_text().count(character))

    app()

`Env` assembles the environment variable name by joining the following components (in-order):

1. The provided `prefix`. In this case, it is `"CHAR_COUNTER_"`.

2. The command and subcommand(s) that lead up to the function being executed.

3. The parameter's CLI name, with the leading `--` stripped, and hyphens `-` replaced with underscores `_`.

Running this code without a specified `--character` results in counting the default `-` character.

    $ python character-counter.py count README.md
    70

By exporting a value to `CHAR_COUNTER_COUNT_CHARACTER`, that value will now be used as the default:

    $ export CHAR_COUNTER_COUNT_CHARACTER=t
    $ python character-counter.py count README.md
    380
    $ python character-counter.py count README.md --character=q
    3