Subcommand sections

Cloup allows you to organize the subcommand of a Group (or, more in general, of a MultiCommand) in multiple help sections. Each such help section is represented by a Section instance, which is just a titled container for commands.

A Section can be:

  • sorted – it lists the commands in alphabetical order

  • unsorted – it lists the commands in the order they are added to the section.

All sections defined by the developer are unsorted by default. You can create a sorted section by passing sorted=True or by using the static method Section.sorted(*commands).

Adding full sections

This is my favourite way of structuring my sections. You can find a runnable example that implements part of the help of Git here. The code below is based on that example.

import cloup
from .commands import (  # import your subcommands
    git_clone, git_init, git_rm, git_sparse_checkout, git_mv,
    git_status, git_log)

@cloup.group('git')
def git():
    return 0

git.section(
    'Start a working area (see also: git help tutorial)',
    git_clone,
    git_init
)
git.section(
    'Work on the current change (see also: git help everyday)',
    git_rm,
    git_sparse_checkout,
    git_mv
)
# Subcommands that are not assigned to a specific section
# populate the "default section"
git.add_command(git_status)
git.add_command(git_log)
Usage: git [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Start a working area (see also: git help tutorial):
  clone            Clone a repository into a new directory
  init             Create an empty Git repository or reinitialize an...

Work on the current change (see also: git help everyday):
  rm               Remove files from the working tree and from the index
  sparse-checkout  Initialize and modify the sparse-checkout
  mv               Move or rename a file, a directory, or a symlink

Other commands:
  log              Show commit logs
  status           Show the working tree status

All commands that are not explicitly assigned to a section are assigned to a default (sorted) section. This section is titled “Other commands”, unless it is the only section defined, in which case cloup.Group behaves like a normal click.Group, naming it just “Commands”.

Each call of Group.section instantiates a new Section and adds it to the Group. Of course, when you add a section, all its commands added to the Group.

In alternative, you can create a list of Section objects and pass it as the sections argument of cloup.group():

import cloup
from cloup import Section

# [...] omitting import/definition of subcommands

SECTIONS = [
    Section('Start a working area (see also: git help tutorial)',
            [git_clone, git_init]),
    Section('Work on the current change (see also: git help everyday)',
            [git_rm, git_sparse_checkout, git_mv])
]

@cloup.group('git', sections=SECTIONS)
def git():
    return 0

Adding subcommands one at a time

In Cloup, all Group methods for adding subcommands, i.e. Group.command, Group.group and Group.add_command, have an additional section argument that you can (optionally) use to assign a subcommand to a Section.

import cloup
from cloup import Section

# Define sections without filling them.
# I'm using a class as a namespace here.
class Sect:
    START_WORKING_AREA = Section(
        'Start a working area (see also: git help tutorial)')
    WORK_CURRENT_CHANGE = Section(
        'Work on the current change (see also: git help everyday)'

@cloup.group('git')
def git():
    return 0

@git.command('init', section=Sect.START_WORKING_AREA)
def git_init():
    pass

@git.command('mv', section=Sect.WORK_CURRENT_CHANGE)
def git_mv():
    pass

Note that – differently from OptionGroup instances – Section instances don’t act as simple markers, they act as containers from the start: they are mutated every time you assign a subcommand to them.