Validation
Typer has builtin argument validation for certain type annotations.
typer_app = typer.Typer()
@typer_app.command()
def foo(age: Annotated[int, typer.Argument(min=0)]):
pass
This works for a select few builtins, but the Typer solution doesn't abstract out validation properly.
Why does the generic typer.Argument
have fields that only have meaning if the annotated type is a number?
The typer.Argument
signature has a ridiculous number of fields that only apply for certain types.
def Argument(
# Parameter
default: Optional[Any] = ...,
*,
callback: Optional[Callable[..., Any]] = None,
metavar: Optional[str] = None,
expose_value: bool = True,
is_eager: bool = False,
envvar: Optional[Union[str, List[str]]] = None,
shell_complete: Optional[
Callable[
[click.Context, click.Parameter, str],
Union[List["click.shell_completion.CompletionItem"], List[str]],
]
] = None,
autocompletion: Optional[Callable[..., Any]] = None,
# Custom type
parser: Optional[Callable[[str], Any]] = None,
# TyperArgument
show_default: Union[bool, str] = True,
show_choices: bool = True,
show_envvar: bool = True,
help: Optional[str] = None,
hidden: bool = False,
# Choice
case_sensitive: bool = True,
# Numbers
min: Optional[Union[int, float]] = None,
max: Optional[Union[int, float]] = None,
clamp: bool = False,
# DateTime
formats: Optional[List[str]] = None,
# File
mode: Optional[str] = None,
encoding: Optional[str] = None,
errors: Optional[str] = "strict",
lazy: Optional[bool] = None,
atomic: bool = False,
# Path
exists: bool = False,
file_okay: bool = True,
dir_okay: bool = True,
writable: bool = False,
readable: bool = True,
resolve_path: bool = False,
allow_dash: bool = False,
path_type: Union[None, Type[str], Type[bytes]] = None,
# Rich settings
rich_help_panel: Union[str, None] = None,
) -> Any:
...
Cyclopts has an explicit validator
field that accepts a function:
cyclopts_app = cyclopts.App()
def age_validator(type_, value: int):
if value < 0:
raise ValueError
@cyclopts_app.command()
def foo(age: Annotated[int, Parameter(validator=age_validator)]):
pass
This solution is similar to how other libraries, like Attrs or Pydantic, perform validation.
Cyclopts has builtin validators for common use-cases.
# Typer
typer.Argument(file_okay=True, exists=True)
# Cyclopts
cyclopts.Parameter(validator=cyclopts.validators.Path(file_okay=True, exists=True))