Как можно отобразить список команд в пределах группы с цепочкой кликов? - PullRequest
1 голос
/ 07 ноября 2019

Я запускаю проект приложения типа CLI pipe, который в конечном итоге будет иметь довольно большую коллекцию команд (которая будет дополнительно расширяться с помощью плагина). В результате я хотел бы классифицировать их в тексте --help:

Вот как это выглядит сейчас:

Usage: my_pipe [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  --help         Show this message and exit.

Commands:
  another_filter     help about that filter
  another_generator  help about that generator
  another_sink       help about that sink
  some_filter        help about this filter
  some_generator     help about this generator
  some_sink          help about this sink

Это более или менее так, как я бы хотелпосмотрите:

Usage: my_pipe [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  --help         Show this message and exit.

Commands:

  Generators:
     some_generator     help about this generator
     another_generator  help about that generator

  Filters:
     some_filter        help about this filter
     another_filter     help about that filter

  Sinks:
     some_sink          help about this sink
     another_sink       help about that sink

Как этого достичь? Обратите внимание, что, кроме взгляда --help, я доволен плоской логической организацией команд. Кроме того, подгруппы не являются опцией, так как они не разрешены внутри chain=True группы.

1 Ответ

1 голос
/ 08 ноября 2019

Если вы наследуете от click.Group, вы можете добавить немного кода для группировки команд и затем показать эти группы в справке.

Пользовательский класс

class GroupedGroup(click.Group):

    def command(self, *args, **kwargs):
        """Gather the command help groups"""
        help_group = kwargs.pop('group', None)
        decorator = super(GroupedGroup, self).command(*args, **kwargs)

        def wrapper(f):
            cmd = decorator(f)
            cmd.help_group = help_group
            return cmd

        return wrapper

    def format_commands(self, ctx, formatter):
        # Modified fom the base class method

        commands = []
        for subcommand in self.list_commands(ctx):
            cmd = self.get_command(ctx, subcommand)
            if not (cmd is None or cmd.hidden):
                commands.append((subcommand, cmd))

        if commands:
            longest = max(len(cmd[0]) for cmd in commands)
            # allow for 3 times the default spacing
            limit = formatter.width - 6 - longest

            groups = {}
            for subcommand, cmd in commands:
                help_str = cmd.get_short_help_str(limit)
                subcommand += ' ' * (longest - len(subcommand))
                groups.setdefault(
                    cmd.help_group, []).append((subcommand, help_str))

            with formatter.section('Commands'):
                for group_name, rows in groups.items():
                    with formatter.section(group_name):
                        formatter.write_dl(rows)

Использование пользовательскогоКласс

Чтобы использовать пользовательский класс, используйте параметр cls для передачи класса в декоратор click.group().

@click.group(cls=GroupedGroup)
def cli():
    """My awesome cli"""

Затем для каждой команды отметьте группу помощи длякоманда, которая будет включена как:

@cli.command(group='A Help Group')
def command():
    """This is a command"""

Как это работает?

Это работает, потому что click - это хорошо спроектированная OO-инфраструктура. Декоратор @click.group() обычно создает экземпляр объекта click.Group, но позволяет изменить это поведение с помощью параметра cls. Так что относительно просто унаследовать от click.Group в нашем собственном классе и переопределить нужные методы.

В этом случае мы переопределим декоратор click.Group.command(), чтобы собрать нужную группу помощи для каждой команды. Затем мы переопределяем метод click.Group.format_commands(), чтобы использовать эти группы при построении справки.

Код теста

import click

@click.group(cls=GroupedGroup)
def cli():
    """My awesome cli"""

@cli.command(group='Generators')
def some_generator():
    """This is Some Generator"""

@cli.command(group='Generators')
def another_generator():
    """This is Another Generator"""

@cli.command(group='Filters')
def some_filter():
    """This is Some Filter"""

@cli.command(group='Filters')
def another_filter():
    """This is Another Filter"""

cli()

Результаты

Usage: test.py [OPTIONS] COMMAND [ARGS]...

  My awesome cli

Options:
  --help  Show this message and exit.

Commands:

  Filters:
    another-filter     This is Another Filter
    some-filter        This is Some Filter

  Generators:
    another-generator  This is Another Generator
    some-generator     This is Some Generator
...