Parameter Validators

In CLI applications, users have the freedom to input a wide range of data. This flexibility can lead to inputs the application does not expect. By coercing the input into a data type (like an int), we are already limiting the input to a certain degree (e.g. "foo" cannot be coerced into an integer). To further restrict the user input, you can populate the validator field of Parameter.

A validator is any callable object (such as a function) that has the signature:

def validator(type_, value: Any) -> None:
    pass  # Raise any exception here if ``value`` is invalid.

Validation happens after the data converter runs. Any of AssertionError, TypeError or ValidationError will be promoted to a cyclopts.ValidationError so that the exception gets presented to the end-user in a nicer way. More than one validator can be supplied as a list to the validator field.

Cyclopts has some builtin common validators in the cyclopts.validators module. See Types for common specific definitions provided as convenient pre-annotated types.

Path

The Path validator ensures certain properties of the parsed pathlib.Path object, such as asserting the file must exist.

from cyclopts import App, Parameter, validators
from typing import Annotated
from pathlib import Path

app = App()

@app.default()
def foo(path: Annotated[Path, Parameter(validator=validators.Path(exists=True))]):
    print(f"File contents:\n{path.read_text()}")

app()
$ echo Hello World > my_file.txt

$ my-script my_file.txt
File contents:
Hello World

$ my-script this_file_does_not_exist.txt
╭─ Error ────────────────────────────────────────────────────────────╮
│ Invalid value "this_file_does_not_exist.txt" for "PATH".           │
│ "this_file_does_not_exist.txt" does not exist.                     │
╰────────────────────────────────────────────────────────────────────╯

See Annotated Path Types for Annotated-Type equivalents of common Path converter/validators.

Number

The Number validator can set minimum and maximum input values.

from cyclopts import App, Parameter, validators
from typing import Annotated

app = App()

@app.default()
def foo(n: Annotated[int, Parameter(validator=validators.Number(gte=0, lt=16))]):
    print(f"Your number in hex is {str(hex(n))[2]}.")

app()
$ my-script 0
Your number in hex is 0.

$ my-script 15
Your number in hex is f.

$ my-script 16
╭─ Error ────────────────────────────────────────────────────────────╮
│ Invalid value "16" for "N". Must be < 16.                          │
╰────────────────────────────────────────────────────────────────────╯

See Annotated Number Types for Annotated-Type equivalents of common Number converter/validators.