Parameters¶
This guide dives deep into Sayer’s parameter system, explaining what each feature is, why you’d use it, and when it’s most appropriate. Examples illustrate real‐world patterns.
1. Core Parameter Types¶
Type | CLI Style | Typical Use Case |
---|---|---|
Option |
--flag value |
Named flags, optional or required toggles, environment fallbacks. |
Argument |
positional values | Natural, required list/sequence inputs (e.g., filenames). |
Env |
environment variables | Secrets, credentials, settings loaded from the environment. |
JsonParam |
JSON string | Complex data structures (e.g., Pydantic models) via JSON. |
1.1 Option
¶
What: A named CLI parameter: --name foo
or flag --verbose
.
Why: Use for any value where clarity and explicit naming help the user. Good for:
- Configuration values (ports, hosts, file paths).
- Feature flags (
--dry-run
,--force
). - Any optional input where a default makes sense.
When:
- You want
--timeout 30
instead of positional30
to avoid ambiguity in order. - You have multiple optional values in one command.
- You need
--help
to clearly annotate each parameter.
Example:
from sayer import Sayer, Option
app = Sayer()
@app.command()
def upload(
file: str = Option(..., help="Path to the file to upload."),
retry: int = Option(3, help="Number of retries on failure."),
force: bool = Option(False, help="Overwrite existing files."),
):
pass
file
is required (Option(...)
).retry
is optional with default3
.force
is a boolean flag (no value needed).
1.2 Argument
¶
What: Positional CLI parameters: cmd input1 input2 ...
.
Why: When order matters, or you want succinct commands:
- File lists:
process file1.txt file2.txt
. - Data points:
compute 1 2 3
.
When:
- Inputs form a natural sequence.
- You expect users to type multiple values quickly.
- You prefer brevity over explicit naming.
Example:
from sayer import Argument
@app.command()
def process(
inputs: list[str] = Argument(
nargs=-1, help="Files to process.")
):
pass
nargs=-1
captures all remaining tokens.
1.3 Env
¶
What: Injects values from environment variables.
Why: For secrets and configuration you don’t want on the CLI:
- API keys
- Database URIs
- Cloud credentials
When:
- You want CI/CD or server automation to provide values via environment.
- You want to avoid revealing secrets in shell history.
Example:
from sayer import Env
@app.command()
def deploy(
token: str = Env(..., envvar="DEPLOY_TOKEN", help="Auth token."),
):
pass
- User runs
export DEPLOY_TOKEN=xyz
once, thendeploy
with no flags.
1.4 JsonParam
¶
What: Accepts a JSON string on the CLI and parses it into Python objects.
Why: Complex nested data (configs, records) without writing a file.
When:
- You need a rich structure: lists of dicts, nested objects.
- You prefer inline JSON over
--opt key1=val1 --opt key2=val2
.
Example:
from sayer.params import JsonParam
@app.command()
def create(
data: MyPydanticModel = JsonParam(help="JSON of the new record."),
):
pass
- CLI:
create '{"name": "Alice", "age": 30}'
2. Annotated Metadata¶
Use typing.Annotated
to group type hints with metadata in one place:
from typing import Annotated
from sayer.params import Option, Argument
@app.command()
def run(
path: Annotated[str, Argument(..., help="Source path.")],
level: Annotated[int, Option(1, help="Verbosity level.")],
):
pass
- Cleaner than mixing defaults and metadata.
- Leverages standard Python typing.
3. Container Types Support¶
Sayer now casts multiple CLI tokens into Python containers with element-wise conversion.
Container | nargs=-1 |
Example CLI | Parsed Python Type |
---|---|---|---|
list[T] |
Yes | prog cmd 1 2 3 |
[1,2,3] |
tuple[T,...] |
Yes | prog cmd 1 2 3 |
(1,2,3) |
tuple[A,B,C] |
Yes | prog cmd a 1 2.5 |
('a',1,2.5) |
set[T] |
Yes | prog cmd a b a |
{'a','b'} |
frozenset[T] |
Yes | prog cmd x y x |
frozenset({'x','y'}) |
dict[K,V] |
Yes | prog cmd k1=1 k2=2 |
{'k1':1,'k2':2} |
Implementation Details:
- Under the hood,
_convert_cli_value_to_type
inspectstyping.get_origin
andget_args
. - Lists/tuples/sets/frozensets: iterate and cast each item.
dict[K,V]
: expects tokenskey=value
, splitting and casting both sides.
4. When to Use What¶
Scenario | Recommended Parameter |
---|---|
Single required value | Option(..., help="...") |
Multiple unnamed values | Argument(nargs=-1) |
Secret/token | Env(..., envvar="...", help="...") |
Rich structured payload | JsonParam() |
Multiple homogeneous numeric inputs | list[int] = Argument(nargs=-1) |
Fixed-format heterogeneous inputs (x,y,z) | tuple[str,int,float] = Argument(nargs=-1) |
Unique values from CLI | set[...] or frozenset[...] |
Key-value option list | dict[str,int] = Argument(nargs=-1) |
5. Execution & Validation Order¶
- Positional Arguments (
Argument
). - Options (
Option
, including boolean flags). - Environment fallback for missing values.
- Container conversion (list/tuple/set/frozenset/dict).
- JSON deserialization (
JsonParam
). - Custom callbacks and
Param.callback
hooks. - Context / State injection.
At each step, type mismatches raise click.BadParameter
.
6. Best Practices¶
- Favor
Option
for clarity if order of args might confuse users. - Use
Argument
for natural, required sequences. - Combine
Option
+Env
for sensitive defaults (--token
or$TOKEN
). - Leverage container types to avoid manual
split()
logic. - Annotate with
Annotated[...]
to keep definitions tidy. - Document every parameter with
help=
.
With these tools, Sayer makes building rich, user-friendly Python CLIs a breeze—embrace the full power of typing, metadata, and automatic conversion!