import click import collections class CategorizedGroup(click.Group): """ Click command group that categorizes subcommands in the help text by the additional 'category' attribute. Commands that don't have a 'category' attribute are grouped under the default category "Commands" as usual. Example usage: `@categorized_group.command('foobar', category='Debug commands')` """ default_category: str def __init__(self, name=None, commands=None, default_category: str = 'Commands', **attrs): super().__init__(name, commands, **attrs) self.commands = commands or collections.OrderedDict() self.default_category = default_category def list_commands(self, ctx): """ List commands in the order they were added to the group. """ return self.commands def command(self, *args, **kwargs): """ Extends the command decorator by setting a category attribute on the command. """ category = kwargs.pop('category', None) orig_decorator = super().command(*args, **kwargs) def decorator(f): cmd = orig_decorator(f) if category: cmd.category = category return cmd return decorator def format_commands(self, ctx, formatter): """ Prints commands in help messages. This version groups them into categories and is based on the original Group.format_commands method. """ categorized_commands = {self.default_category: []} # Collect subcommands grouped by category for subcommand in self.list_commands(ctx): cmd = self.get_command(ctx, subcommand) if cmd is None or cmd.hidden: continue category = getattr(cmd, 'category', self.default_category) if categorized_commands.get(category) is None: categorized_commands[category] = [] categorized_commands[category].append((subcommand, cmd)) # Print help sections for each subcommand category for category, subcommands in categorized_commands.items(): if not len(subcommands): continue limit = formatter.width - 6 - max(len(cmd[0]) for cmd in subcommands) rows = [] for subcommand, cmd in subcommands: help_str = cmd.get_short_help_str(limit) rows.append((subcommand, help_str)) with formatter.section(category): formatter.write_dl(rows)