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