Chapter 1: Commands and Groups: The Building Blocks
Welcome to your first step in learning Click! Imagine you want to create your own command-line tool, maybe something like git
or docker
. How do you tell your program what to do when someone types git commit
or docker build
? That’s where Commands and Groups come in. They are the fundamental building blocks for any Click application.
Think about a simple tool. Maybe you want a program that can greet someone. You’d type greet Alice
in your terminal, and it would print “Hello Alice!”. In Click, this single action, “greet”, would be represented by a Command
.
Now, what if your tool needed to do more than one thing? Maybe besides greeting, it could also say goodbye. You might want to type mytool greet Alice
or mytool goodbye Bob
. The main mytool
part acts like a container or a menu, holding the different actions (greet
, goodbye
). This container is what Click calls a Group
.
So:
Command
: Represents a single action your tool can perform.Group
: Represents a collection of related actions (Commands or other Groups).
Let’s dive in and see how to create them!
Your First Command
Creating a command in Click is surprisingly simple. You basically write a normal Python function and then “decorate” it to tell Click it’s a command-line command.
Let’s make a command that just prints “Hello World!”.
# hello_app.py
import click
@click.command()
def hello():
"""A simple command that says Hello World"""
print("Hello World!")
if __name__ == '__main__':
hello()
Let’s break this down:
import click
: We need to import the Click library first.@click.command()
: This is the magic part! It’s called a decorator. It transforms the Python functionhello()
right below it into a ClickCommand
object. We’ll learn more about Decorators in the next chapter, but for now, just know this line turnshello
into something Click understands as a command.def hello(): ...
: This is a standard Python function. The code inside this function is what will run when you execute the command from your terminal."""A simple command that says Hello World"""
: This is a docstring. Click cleverly uses the function’s docstring as the help text for the command!if __name__ == '__main__': hello()
: This standard Python construct checks if the script is being run directly. If it is, it calls ourhello
command function (which is now actually a ClickCommand
object).
Try running it! Save the code above as hello_app.py
. Open your terminal in the same directory and run:
$ python hello_app.py
Hello World!
It works! You just created your first command-line command with Click.
Bonus: Automatic Help!
Click automatically generates help screens for you. Try running your command with --help
:
$ python hello_app.py --help
Usage: hello_app.py [OPTIONS]
A simple command that says Hello World
Options:
--help Show this message and exit.
See? Click used the docstring we wrote (A simple command that says Hello World
) and added a standard --help
option for free!
Grouping Commands
Okay, one command is nice, but real tools often have multiple commands. Like git
has commit
, pull
, push
, etc. Let’s say we want our tool to have two commands: hello
and goodbye
.
We need a way to group these commands together. That’s what click.group()
is for. A Group
acts as the main entry point and can have other commands attached to it.
# multi_app.py
import click
# 1. Create the main group
@click.group()
def cli():
"""A simple tool with multiple commands."""
pass # The group function itself doesn't need to do anything
# 2. Define the 'hello' command
@click.command()
def hello():
"""Says Hello World"""
print("Hello World!")
# 3. Define the 'goodbye' command
@click.command()
def goodbye():
"""Says Goodbye World"""
print("Goodbye World!")
# 4. Attach the commands to the group
cli.add_command(hello)
cli.add_command(goodbye)
if __name__ == '__main__':
cli() # Run the main group
What’s changed?
- We created a function
cli
and decorated it with@click.group()
. This makescli
our main entry point, a container for other commands. Notice the function body is justpass
– often, the group function itself doesn’t need logic; its job is to hold other commands. - We defined
hello
andgoodbye
just like before, using@click.command()
. - Crucially, we attached our commands to the group:
cli.add_command(hello)
andcli.add_command(goodbye)
. This tells Click thathello
andgoodbye
are subcommands ofcli
. - Finally, in the
if __name__ == '__main__':
block, we runcli()
, our main group.
Let’s run this! Save it as multi_app.py
.
First, check the main help screen:
$ python multi_app.py --help
Usage: multi_app.py [OPTIONS] COMMAND [ARGS]...
A simple tool with multiple commands.
Options:
--help Show this message and exit.
Commands:
goodbye Says Goodbye World
hello Says Hello World
Look! Click now lists goodbye
and hello
under “Commands”. It automatically figured out their names from the function names (goodbye
, hello
) and their help text from their docstrings.
Now, run the specific commands:
$ python multi_app.py hello
Hello World!
$ python multi_app.py goodbye
Goodbye World!
You’ve successfully created a multi-command CLI tool!
(Self-promotion: There’s an even shorter way to attach commands using decorators directly on the group, which we’ll see in Decorators!)
How It Works Under the Hood
What’s really happening when you use @click.command()
or @click.group()
?
-
Decoration: The decorator (
@click.command
or@click.group
) takes your Python function (hello
,goodbye
,cli
). It wraps this function inside a Click object – either aCommand
instance or aGroup
instance (which is actually a special type ofCommand
). These objects store your original function as thecallback
to be executed later. They also store metadata like the command name (derived from the function name) and the help text (from the docstring). You can find the code for these decorators indecorators.py
and theCommand
/Group
classes incore.py
. -
Execution: When you run
python multi_app.py hello
, Python executes thecli()
call at the bottom. Sincecli
is aGroup
object created by Click, it knows how to parse the command-line arguments (hello
in this case). -
Parsing & Dispatch: The
cli
group looks at the first argument (hello
). It checks its list of registered subcommands (which we added usingcli.add_command
). It finds a match with thehello
command object. -
Callback: The
cli
group then invokes thehello
command object. Thehello
command object, in turn, calls the original Python function (hello()
) that it stored earlier as itscallback
.
Here’s a simplified view of what happens when you run python multi_app.py hello
:
sequenceDiagram
participant User
participant Terminal
participant PythonScript (multi_app.py)
participant ClickRuntime
participant cli_Group as cli (Group Object)
participant hello_Command as hello (Command Object)
User->>Terminal: python multi_app.py hello
Terminal->>PythonScript: Executes script with args ["hello"]
PythonScript->>ClickRuntime: Calls cli() entry point
ClickRuntime->>cli_Group: Asks to handle args ["hello"]
cli_Group->>cli_Group: Parses args, identifies "hello" as subcommand
cli_Group->>hello_Command: Invokes the 'hello' command
hello_Command->>hello_Command: Executes its callback (the original hello() function)
hello_Command-->>PythonScript: Prints "Hello World!"
PythonScript-->>Terminal: Shows output
Terminal-->>User: Displays "Hello World!"
This process of parsing arguments and calling the right function based on the command structure is the core job of Click, making it easy for you to just focus on writing the functions for each command.
Conclusion
You’ve learned about the two most fundamental concepts in Click:
Command
: Represents a single action, created by decorating a function with@click.command()
.Group
: Acts as a container for multiple commands (or other groups), created with@click.group()
. Groups allow you to structure your CLI application logically.
We saw how Click uses decorators to transform simple Python functions into powerful command-line interface components, automatically handling things like help text generation and command dispatching.
Commands and Groups form the basic structure, but how do we pass information into our commands (like git commit -m "My message"
)? And what other cool things can decorators do? We’ll explore that starting with a deeper look at decorators in the next chapter!
Next up: Chapter 2: Decorators
Generated by AI Codebase Knowledge Builder