Щелкните: Настройте обработку ошибок с отсутствующим аргументом, переопределив функцию показа UsageError.

В настоящее время я пытаюсь настроить обработку ошибок, когда команда была дана без предоставления необходимых аргументов с помощью Click. В соответствии с этот вопрос SO это можно сделать, переопределив функцию show click.exceptions.UsageError. Однако я попытался изменить предоставленное там решение, но не смог заставить его работать.

В моем случае я хочу иметь возможность получить команду, которая должна быть выполнена (но не удалась из-за отсутствия аргументов) и в зависимости от введенной команды хочу продолжить обработку. Мой пример кода выглядит так:

@click.group(cls=MyGroup)
def myapp():
    pass

@myapp.command()
@click.argument('myarg',type=str)
def mycommand(myarg: str) -> None:
    do_stuff(myarg)

Поэтому, если команда была чем-то вроде myapp mycommand и в ней отсутствуют необходимые аргументы, я хочу обработать ее индивидуально. Я искал какое-то время, но не смог понять, КАК получить команду (я пытался передать контекст, но, насколько я читал, UsageError не получает никакого контекста при инициализации).

Буду признателен за любую подсказку или идею.

РЕДАКТИРОВАТЬ: реализация myGroup выглядит так:

class myGroup(click.Group):
"""
Customize help order and get_command
https://stackoverflow.com/a/47984810/713980
"""

def __init__(self, *args, **kwargs):
    self.help_priorities = {}
    super(myGroup, self).__init__(*args, **kwargs)

def get_help(self, ctx):
    self.list_commands = self.list_commands_for_help
    return super(myGroup, self).get_help(ctx)

def list_commands_for_help(self, ctx):
    """reorder the list of commands when listing the help"""
    commands = super(myGroup, self).list_commands(ctx)
    return (c[1] for c in sorted((self.help_priorities.get(command, 1000), command) for command in commands))

def command(self, *args, **kwargs):
    """Behaves the same as `click.Group.command()` except capture
    a priority for listing command names in help.
    """
    help_priority = kwargs.pop('help_priority', 1000)
    help_priorities = self.help_priorities

    def decorator(f):
        cmd = super(myGroup, self).command(*args, **kwargs)(f)
        help_priorities[cmd.name] = help_priority
        return cmd
    return decorator

def get_command(self, ctx, cmd_name):
    rv = click.Group.get_command(self, ctx, cmd_name)
    if rv is not None:
        return rv
    sim_commands = most_sim_com(cmd_name, COMMANDS)

    matches = [cmd for cmd in self.list_commands(ctx) if cmd in sim_commands]
    if not matches:
        ctx.fail(click.style('Unknown command and no similar command was found!', fg='red'))
    elif len(matches) == 1:
        click.echo(click.style(f'Unknown command! Will use best match {matches[0]}.', fg='red'))
        return click.Group.get_command(self, ctx, matches[0])
    ctx.fail(click.style(f'Unknown command. Most similar commands were {", ".join(sorted(matches))}', fg='red'))

person Farwent    schedule 27.04.2020    source источник
comment
И снова здравствуйте. Не могли бы вы опубликовать свою реализацию MyGroup, пожалуйста?   -  person afterburner    schedule 28.04.2020
comment
Эй;), я отредактировал свой пост выше.   -  person Farwent    schedule 28.04.2020
comment
Итак, чтобы уточнить, вы хотите иметь собственное сообщение об ошибке, если myarg не передается в ваш вызов команды?   -  person afterburner    schedule 28.04.2020
comment
Да, но, кроме того, я хочу знать, какая команда была вызвана, чтобы выполнять множество других действий в зависимости от этой команды.   -  person Farwent    schedule 28.04.2020


Ответы (1)


Это первый набросок и самое наивное решение, которое я могу придумать, поэтому оно может изменить его, если не полностью решит вашу проблему. Поможет ли изменение вашего кода на что-то вроде этого?

@click.group(cls=MyGroup)
def myapp():
    pass

@myapp.command()
@click.argument('myarg',type=str, required=False)
def mycommand(myarg: str=None) -> None:
    validate_my_command(myarg) # this is where you do your custom logic and error message handling

Преимущество этого способа заключается в том, что он является явным и соответствует способу, предложенному Click. Однако, если вы хотите сделать это для каждой команды, мы можем рассмотреть более сложные подходы.

Дайте мне знать, что вы думаете

person afterburner    schedule 28.04.2020
comment
Крутое, красивое решение, не думал о настройке, необходимой для False, это очень интуитивно понятно. Я думаю, что буду использовать это и для других своих команд и избегаю реализации чего-либо слишком сложного. Из любопытства: на что был бы похож более сложный подход? - person Farwent; 29.04.2020
comment
Я не мог тебе сказать. Я представляю что-то вроде украшения команды щелчка, которая выполняет эту проверку для вас, что звучит выполнимо. Или сверните свою собственную реализацию Command, но она может меняться всякий раз, когда изменяется Click API (не так часто, но о чем следует помнить). - person afterburner; 29.04.2020
comment
Создание собственного декоратора звучит довольно интересно. Но тем не менее еще раз спасибо ;) - person Farwent; 29.04.2020