Chapter 3: Parameter (Option / Argument) - Giving Your Commands Input
In the last chapter, Decorators, we saw how decorators like @click.command()
and @click.option()
act like magic wands, transforming our Python functions into CLI commands and adding features like command-line options.
But how do our commands actually receive information from the user? If we have a command greet
, how do we tell it who to greet, like greet --name Alice
? Or if we have a copy
command, how do we specify the source and destination files, like copy report.txt backup.txt
?
This is where Parameters come in. Parameters define the inputs your commands can accept, just like arguments define the inputs for a regular Python function. Click handles parsing these inputs from the command line, validating them, and making them available to your command function.
There are two main types of parameters in Click:
- Options: These are usually preceded by flags like
--verbose
or-f
. They are often optional and can either take a value (like--name Alice
) or act as simple on/off switches (like--verbose
). You define them using the@click.option()
decorator. - Arguments: These are typically positional values that come after any options. They often represent required inputs, like a filename (
report.txt
). You define them using the@click.argument()
decorator.
Let’s see how to use them!
Options: The Named Inputs (@click.option
)
Think of options like keyword arguments in Python functions. In def greet(name="World"):
, name
is a keyword argument with a default value. Options serve a similar purpose for your CLI.
Let’s modify our hello
command from the previous chapter to accept a --name
option.
# greet_app.py
import click
@click.group()
def cli():
"""A simple tool with a greeting command."""
pass
@cli.command()
@click.option('--name', default='World', help='Who to greet.')
def hello(name): # <-- The 'name' parameter matches the option
"""Greets the person specified by the --name option."""
print(f"Hello {name}!")
if __name__ == '__main__':
cli()
Let’s break down the new parts:
@click.option('--name', default='World', help='Who to greet.')
: This decorator defines an option.'--name'
: This is the primary name of the option on the command line.default='World'
: If the user doesn’t provide the--name
option, the valueWorld
will be used.help='Who to greet.'
: This text will appear in the help message for thehello
command.
def hello(name):
: Notice how thehello
function now accepts an argument namedname
. Click cleverly matches the option name (name
) to the function parameter name and passes the value automatically!
Try running it!
First, check the help message for the hello
command:
$ python greet_app.py hello --help
Usage: greet_app.py hello [OPTIONS]
Greets the person specified by the --name option.
Options:
--name TEXT Who to greet. [default: World]
--help Show this message and exit.
See? Click added our --name
option to the help screen, including the help text and default value we provided. The TEXT
part indicates the type of value expected (we’ll cover types in ParamType).
Now, run it with and without the option:
$ python greet_app.py hello
Hello World!
$ python greet_app.py hello --name Alice
Hello Alice!
It works perfectly! Click parsed the --name Alice
option and passed "Alice"
to our hello
function’s name
parameter. When we didn’t provide the option, it used the default value "World"
.
Option Flavors: Short Names and Flags
Options can have variations:
- Short Names: You can provide shorter aliases, like
-n
for--name
. - Flags: Options that don’t take a value but act as switches (e.g.,
--verbose
).
Let’s add a short name -n
to our --name
option and a --shout
flag to make the greeting uppercase.
# greet_app_v2.py
import click
@click.group()
def cli():
"""A simple tool with a greeting command."""
pass
@cli.command()
@click.option('--name', '-n', default='World', help='Who to greet.') # Added '-n'
@click.option('--shout', is_flag=True, help='Greet loudly.') # Added '--shout' flag
def hello(name, shout): # <-- Function now accepts 'shout' too
"""Greets the person, optionally shouting."""
greeting = f"Hello {name}!"
if shout:
greeting = greeting.upper()
print(greeting)
if __name__ == '__main__':
cli()
Changes:
@click.option('--name', '-n', ...)
: We added'-n'
as the second argument to the decorator. Now, both--name
and-n
work.@click.option('--shout', is_flag=True, ...)
: This defines a flag.is_flag=True
tells Click this option doesn’t take a value; its presence makes the corresponding parameterTrue
, otherwise it’sFalse
.def hello(name, shout):
: The function signature is updated to accept theshout
parameter.
Run it again!
$ python greet_app_v2.py hello -n Bob
Hello Bob!
$ python greet_app_v2.py hello --name Carol --shout
HELLO CAROL!
$ python greet_app_v2.py hello --shout
HELLO WORLD!
Flags and short names make your CLI more flexible and conventional!
Arguments: The Positional Inputs (@click.argument
)
Arguments are like positional arguments in Python functions. In def copy(src, dst):
, src
and dst
are required positional arguments. Click arguments usually represent mandatory inputs that follow the command and any options.
Let’s create a simple command that takes two arguments, SRC
and DST
, representing source and destination files (though we’ll just print them for now).
# copy_app.py
import click
@click.command()
@click.argument('src') # Defines the first argument
@click.argument('dst') # Defines the second argument
def copy(src, dst): # Function parameters match argument names
"""Copies SRC file to DST."""
print(f"Pretending to copy '{src}' to '{dst}'")
if __name__ == '__main__':
copy()
What’s happening here?
@click.argument('src')
: Defines a positional argument namedsrc
. By default, arguments are required. The name'src'
is used both internally and often capitalized (SRC
) in help messages by convention.@click.argument('dst')
: Defines the second required positional argument.def copy(src, dst):
: The function parameterssrc
anddst
receive the values provided on the command line in the order they appear.
Let’s try it!
First, see what happens if we forget the arguments:
$ python copy_app.py
Usage: copy_app.py [OPTIONS] SRC DST
Try 'copy_app.py --help' for help.
Error: Missing argument 'SRC'.
Click automatically detects the missing argument and gives a helpful error message!
Now, provide the arguments:
$ python copy_app.py report.txt backup/report.txt
Pretending to copy 'report.txt' to 'backup/report.txt'
Click correctly captured the positional arguments and passed them to our copy
function.
Arguments are essential for inputs that are fundamental to the command’s operation, like the files to operate on. Options are better suited for modifying the command’s behavior.
(Note: Arguments can also be made optional or accept variable numbers of inputs, often involving the required
and nargs
settings, which tie into concepts we’ll explore more in ParamType.)
How Parameters Work Together
When you run a command like python greet_app_v2.py hello --shout -n Alice
, Click performs a sequence of steps:
- Parsing: Click looks at the command-line arguments (
sys.argv
) provided by the operating system:['greet_app_v2.py', 'hello', '--shout', '-n', 'Alice']
. - Command Identification: It identifies
hello
as the command to execute. - Parameter Matching: It scans the remaining arguments (
['--shout', '-n', 'Alice']
).- It sees
--shout
. It looks up the parameters defined for thehello
command (using the@click.option
and@click.argument
decorators). It finds theshout
option definition (which hasis_flag=True
). It marks the value forshout
asTrue
. - It sees
-n
. It finds thename
option definition (which includes-n
as an alias and expects a value). - It sees
Alice
. Since the previous token (-n
) expected a value, Click associates"Alice"
with the-n
(and thus--name
) option. It marks the value forname
as"Alice"
.
- It sees
- Validation & Conversion: Click checks if all required parameters are present (they are). It also performs type conversion (though in this case, the default is string, which matches “Alice”). We’ll see more complex conversions in the next chapter.
- Function Call: Finally, Click calls the command’s underlying Python function (
hello
) with the collected values as keyword arguments:hello(name='Alice', shout=True)
.
Here’s a simplified view of the process:
sequenceDiagram
participant User
participant Terminal
participant PythonScript as python greet_app_v2.py
participant ClickRuntime
participant hello_func as hello(name, shout)
User->>Terminal: python greet_app_v2.py hello --shout -n Alice
Terminal->>PythonScript: Executes script with args ["hello", "--shout", "-n", "Alice"]
PythonScript->>ClickRuntime: Calls cli() entry point
ClickRuntime->>ClickRuntime: Parses args, finds 'hello' command
ClickRuntime->>ClickRuntime: Identifies '--shout' as flag for 'shout' parameter (value=True)
ClickRuntime->>ClickRuntime: Identifies '-n' as option for 'name' parameter
ClickRuntime->>ClickRuntime: Consumes 'Alice' as value for '-n'/'name' parameter (value="Alice")
ClickRuntime->>ClickRuntime: Validates parameters, performs type conversion
ClickRuntime->>hello_func: Calls callback: hello(name="Alice", shout=True)
hello_func-->>PythonScript: Prints "HELLO ALICE!"
PythonScript-->>Terminal: Shows output
Terminal-->>User: Displays "HELLO ALICE!"
Under the Hood: Decorators and Parameter Objects
How do @click.option
and @click.argument
actually work with @click.command
?
- Parameter Definition (
decorators.py
,core.py
): When you use@click.option(...)
or@click.argument(...)
, these functions (defined inclick/decorators.py
) create instances of theOption
orArgument
classes (defined inclick/core.py
). These objects store all the configuration you provided (like--name
,-n
,default='World'
,is_flag=True
, etc.). - Attaching to Function (
decorators.py
): Crucially, these decorators don’t immediately add the parameters to a command. Instead, they attach the createdOption
orArgument
object to the function they are decorating. Click uses a helper mechanism (like the internal_param_memo
function which adds to a__click_params__
list) to store these parameter objects on the function object temporarily. - Command Creation (
decorators.py
,core.py
): The@click.command()
decorator (or@group.command()
) runs after all the@option
and@argument
decorators for that function. It looks for the attached parameter objects (the__click_params__
list). It gathers these objects and passes them to the constructor of theCommand
(orGroup
) object it creates. TheCommand
object stores these parameters in itsparams
attribute. - Parsing (
parser.py
,core.py
): When the command is invoked, theCommand
object uses itsparams
list to configure an internal parser (historically based on Python’soptparse
, seeclick/parser.py
). This parser processes the command-line string (sys.argv
) according to the rules defined by theOption
andArgument
objects in theparams
list. - Callback Invocation (
core.py
): After parsing and validation, Click takes the resulting values and calls the original Python function (stored as theCommand.callback
), passing the values as arguments.
So, the decorators work together: @option
/@argument
define the parameters and temporarily attach them to the function, while @command
collects these definitions and builds the final Command
object, ready for parsing.
Conclusion
You’ve learned how to make your Click commands interactive by defining inputs using Parameters:
- Options (
@click.option
): Named inputs, often optional, specified with flags (--name
,-n
). Great for controlling behavior (like--verbose
,--shout
) or providing specific pieces of data (--output file.txt
). - Arguments (
@click.argument
): Positional inputs, often required, that follow options (input.csv
). Ideal for core data the command operates on (like source/destination files).
You saw how Click uses decorators to define these parameters and automatically handles parsing the command line, providing default values, generating help messages, and passing the final values to your Python function.
But what if you want an option to accept only numbers? Or a choice from a predefined list? Or maybe an argument that represents a file path that must exist? Click handles this through Parameter Types. Let’s explore those next!
Next up: Chapter 4: ParamType
Generated by AI Codebase Knowledge Builder