How to list Click CLI commands in the order they're defined
click
is a Python module used to create command-line interfaces (CLI) for scripts and applications.
In the sections below, we’ll look at a method for making sure that click
CLI commands are listed in the order they were defined when we invoke the CLI or --help
flag.
You can skip ahead to the explanation for how our solution works here.
Building a CLI with Click
Let’s look at a click
CLI with a couple of commands:
#!/usr/bin/env python3
import click
@click.group()
def cli():
pass
@cli.command(help='This should be first.')
def xyz_first():
pass
@cli.command(help='This should be second.')
def abc_second():
pass
cli()
If we run this script, we’ll see that the commands xyz-first
and abc-second
aren’t displayed in the order that they were declared:
$ ./cli.py
Usage: cli.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
abc-second This should be second.
xyz-first This should be first.
Ordering commands
If we want to order those commands in the order that we define their functions, we can subclass click.Group
:
import click
class OrderCommands(click.Group):
def list_commands(self, ctx: click.Context) -> list[str]:
return list(self.commands)
Then, when declaring our commands, we pass our new class as a cls
parameter to click.group()
:
@click.group(cls=OrderCommands)
def cli():
pass
@cli.command(help='This should be first.')
def xyz_first():
pass
@cli.command(help='This should be second.')
def abc_second():
pass
Finally, we’ll see that our commands are listed in the order they were defined:
$ ./cli.py
Usage: cli.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
xyz-first This should be first.
abc-second This should be second.
How it works
A click.Group
object has an attribute commands
of type dict[str, click.Command]
. Normally, Group.list_commands()
just returns a sorted()
list of keys from the commands
attribute.
Here’s Group
’s list_commands()
method:
def list_commands(self, ctx: click.Context) -> list[str]:
return sorted(self.commands)
As an implementation detail of PEP 468, dict
’s keys are sorted in the order that they were created. The keys act similar to how a list
orders its items, instead of being unordered like a set
. This is the default behavior in Python 3.6 and up.
That means we can make Group.list_commands()
just return the keys to the Group.commands
attribute in order to preserve the order that our click
commands were defined:
def list_commands(self, ctx: click.Context) -> list[str]:
return list(self.commands)