Options & Arguments¶
Arguments¶
Positional arguments are parameters with no default value:
@command()
def copy(src: str, dst: str) -> None:
"""Copy SRC to DST."""
Usage:
myapp copy file.txt /tmp/
Variadic arguments collect all remaining tokens:
@command()
def add(*files: str) -> None:
"""Stage files."""
Usage:
myapp add file1.py file2.py file3.py
Options¶
Parameters with a default value become CLI options:
@command()
def greet(name: str, template: str = "Hello, {}!") -> None:
"""Greet someone."""
print(template.format(name))
Usage:
myapp greet Alice
myapp greet Alice --template "Hi, {}!"
myapp greet Alice -t "Hi, {}!" # auto-generated short alias
Boolean flags¶
bool options default to False and are toggled by passing the flag:
@command()
def build(release: bool = False) -> None:
"""Build the project."""
Usage:
myapp build # release = False
myapp build --release # release = True
Repeatable options¶
list[T] options accept the flag multiple times:
@command()
def publish(tag: list[str] = []) -> None:
"""Publish with tags."""
Usage:
myapp publish --tag latest --tag stable
# tag = ["latest", "stable"]
Short aliases¶
Xclif auto-generates a single-char short alias for each option using the first available
character of the option name. --template → -t, --release → -r, etc.
If the first character is taken by an implicit option (-h, -v), Xclif tries subsequent
characters. Use Option(name=...) inside Annotated to override the flag name explicitly —
see per-parameter metadata below.
Interspersed options¶
Options and positional arguments may appear in any order at the same command level:
myapp greet --template "Hi!" Alice
myapp greet Alice --template "Hi!" # both valid
Config-backed parameters¶
Parameters annotated with WithConfig[T] fall back to environment variables and config
files when not supplied on the CLI:
from xclif import WithConfig, command
@command()
def _(name: WithConfig[str], greeting: WithConfig[str] = "Hello") -> None:
...
See Configuration for full details on priority order, env var naming, and config file format.
Per-parameter metadata¶
Use Arg and Option inside Annotated to attach descriptions or override
display names and flag names:
from typing import Annotated
from xclif import Arg, Option, command
@command()
def copy(
src: Annotated[str, Arg(description="Source file", name="SRC")],
dst: Annotated[str, Arg(description="Destination path", name="DST")],
) -> None:
"""Copy SRC to DST."""
@command()
def build(
dry_run: Annotated[bool, Option(description="Skip execution", name="dry-run")] = False,
) -> None:
"""Build the project."""
Arg fields:
description— text shown next to the argument in help outputname— display name in help (e.g.SRCinstead ofsrc); does not affect parsing
Option fields:
description— text shown next to the flag in help outputname— overrides the CLI flag name (e.g.dry-run→--dry-run). The Python kwarg name passed to the function is unchanged.
Both can be combined with WithConfig. Note that WithConfig[str] is sugar for
Annotated[str, WithConfig()] — when combining with Arg or Option you must
use the full Annotated form:
name: Annotated[str, Arg(description="Person to greet"), WithConfig()]
Constrained choices¶
Use Literal to restrict an argument or option to a fixed set of string values.
Xclif validates the input and shows the valid choices in help output:
from typing import Annotated, Literal
from xclif import command
@command()
def completions(shell: Literal["bash", "zsh", "fish"]) -> None:
"""Generate shell completion script."""
...
Usage:
myapp completions bash # ok
myapp completions nushell # error: expected one of: bash|zsh|fish
Help output displays the choices inline:
Usage: myapp completions [bash|zsh|fish]
Only Literal of strings is supported. Mixed-type Literals (e.g.
Literal["a", 1]) are not supported and will raise a TypeError at
command construction time.
The -- separator¶
-- stops all option parsing. Everything after it is treated as raw positional arguments:
myapp run -- --some-flag-for-subprocess