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.

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()
def xyz_first():
  pass


@cli.command()
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
  xyz-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 click.group() a cls parameter that points to our new class:

@click.group(cls=OrderCommands)
def cli():
  pass


@cli.command()
def xyz_first():
  pass


@cli.command()
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
  abc-second

How it works

A click.Group has an attribute commands of type dict[str, click.Command]. Normally, click.Group just returns a sorted() list of keys in commands.

Here’s click.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)