Python Click: пользовательское сообщение об ошибке

Я использую отличную библиотеку Python Click для обработки параметров командной строки в моем инструменте. Вот упрощенная версия моего кода (полный скрипт здесь):

@click.command(
    context_settings = dict( help_option_names = ['-h', '--help'] )
)
@click.argument('analysis_dir',
                 type = click.Path(exists=True),
                 nargs = -1,
                 required = True,
                 metavar = "<analysis directory>"
)

def mytool(analysis_dir):
   """ Do stuff """

if __name__ == "__main__":
    mytool()

Если кто-то запускает команду без каких-либо флагов, он получает сообщение об ошибке по умолчанию:

$ mytool

Usage: mytool [OPTIONS] <analysis directory>

Error: Missing argument "analysis_dir".

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

Есть простой способ сделать это? Я знаю, что мог бы удалить атрибут required и обработать эту логику в основной функции, но для такого незначительного дополнения это кажется хакерским.


person ewels    schedule 20.09.2016    source источник
comment
У меня точно такая же проблема. Я хотел бы либо автоматически распечатать справку, если произойдет ошибка, либо, по крайней мере, сообщить пользователю, как распечатать справку. В противном случае мы должны предположить, что все, кто использует наш инструмент, знакомы с мышлением на основе Unix. Мне пока нравится щелкать, но я разочарован тем, что на этот вопрос нет ответа, потому что я хочу что-то, что поможет создать инструменты, которые могут легко использовать новички.   -  person user1677663    schedule 30.03.2017
comment
Только что нашел это, что может быть решением: stackoverflow.com/questions/35642202/   -  person user1677663    schedule 30.03.2017
comment
Нет, это не сработало.   -  person user1677663    schedule 30.03.2017
comment
Как насчет click.pocoo.org/6 /exceptions/#что-если-я-не-хочу-этого ?   -  person blubberdiblub    schedule 29.04.2017
comment
Да, я смотрел именно на этот @blubberdiblub, но не мог понять, как его реализовать, если не используется синтаксис command.main(['command-name', 'args', 'go', 'here']). Я нигде не нашел примеров и сдался. Если у вас есть какие-либо указания о том, как применить это с синтаксисом в приведенном выше примере, это было бы очень полезно!   -  person ewels    schedule 02.05.2017


Ответы (1)


Создание сообщения для большинства ошибок в python-click обрабатывается методом show класса UsageError: click.exceptions.UsageError.show.

Таким образом, если вы переопределите этот метод, вы сможете создать свое собственное сообщение об ошибке. Ниже приведен пример настройки, которая добавляет меню справки к любому сообщению об ошибке, которое отвечает на этот SO вопрос:

def modify_usage_error(main_command):
    '''
        a method to append the help menu to an usage error

    :param main_command: top-level group or command object constructed by click wrapper 
    :return: None
    '''

    from click._compat import get_text_stderr
    from click.utils import echo
    def show(self, file=None):
        import sys
        if file is None:
            file = get_text_stderr()
        color = None
        if self.ctx is not None:
            color = self.ctx.color
            echo(self.ctx.get_usage() + '\n', file=file, color=color)
        echo('Error: %s\n' % self.format_message(), file=file, color=color)
        sys.argv = [sys.argv[0]]
        main_command()

    click.exceptions.UsageError.show = show

После того, как вы определите свою основную команду, вы можете запустить скрипт модификатора:

import click
@click.group()
def cli():
    pass

modify_usage_error(cli)

Я не исследовал, есть ли вызовы ClickException во время выполнения, кроме ошибок использования. Если они есть, вам, возможно, потребуется изменить свой собственный обработчик ошибок, чтобы сначала проверить, что ctx является атрибутом, прежде чем добавлять строку click.exceptions.ClickException.show = show, поскольку не похоже, что ClickException получает ctx при инициализации.

person R J    schedule 11.05.2017
comment
Фантастика, спасибо RJ! Это работает отлично, за одним исключением — мне пришлось удалить последний вызов функции main_command(), так как это вызвало ошибку рекурсии. - person ewels; 12.05.2017
comment
Это было хорошо! github.com/ewels/MultiQC/commit/62cc60 У меня был низкий уровень раздражения по поводу того, что я не могу сделать это уже семь месяцев! Спасибо еще раз! - person ewels; 12.05.2017
comment
Любое предложение, как перезаписать все типы исключений кликов одновременно? В идеале, чтобы он не ломался, когда вы нажимаете добавить новые исключения? - person omni; 01.10.2018
comment
Вы можете попытаться перебрать dir(click.exceptions), исключить встроенные методы stackoverflow.com/a/21542780/4941585, а затем setattr(объект, 'показать', показать). docs.python.org/3/library/functions.html#setattr Однако это может привести к неожиданному поведению. - person R J; 07.10.2018