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)