Есть ли способ автоматически обрабатывать исключения с помощью Python Click?

В документации по обработке исключений Click упоминается, что определенные виды исключений, такие как Abort, EOFError а также KeyboardInterrupt автоматически изящно обрабатываются рамкой.

Для приложения, которое я пишу, есть много моментов, из которых можно генерировать исключения. Завершение приложения - правильный шаг, но печать трассировки стека - нет. Я всегда мог сделать это вручную:

@cli.command()
def somecommand:
  try:
    # ...
  except Exception as e:
    click.echo(e)

Однако есть ли способ, чтобы Click обрабатывал все исключения автоматически?

2 ответа

Решение

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

Наша точка входа выглядит примерно так:

 @click.group()
 @click.pass_context
 def entry_point(ctx):
      ctx.obj = {"example": "This could be the configuration"}

Мы используем его для запуска глобального кода, например, для настройки context, но вы также можете определить пустой метод, который ничего не делает. Другие команды могут быть добавлены в эту группу команд либо с помощью @entry_point.command() декоратор или entry_point.add_command(cmd),

Для обработки исключений мы переносим entry_point в другом методе, который обрабатывает исключения:

 def safe_entry_point():
      try:
          entry_point()
      except Exception as e:
          click.echo(e)

В setup.pyМы настраиваем точку входа для CLI и указываем ее на оболочку:

 entry_points={
    'console_scripts': [
        'cli = my.package:safe_entry_point'
    ]
}

Команды CLI могут быть выполнены через его группу команд: например, cli command,

Там могут быть более элегантные решения, но именно так мы и решили. Хотя он представляет группу команд как элемент самого высокого уровня в вашем CLI, но он позволяет нам обрабатывать все исключения в одном месте без необходимости дублировать нашу обработку ошибок в каждой команде.

Если вы хотите обрабатывать исключение только для определенных команд CLI. Вы можете использовать другой декоратор для обработки исключений.

Вот пример:

      import click
from functools import wraps, partial


class NumberTooLarge(Exception):
    pass

def catch_exception(func=None, *, handle):
    if not func:
        return partial(catch_exception, handle=handle)

    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except handle as e:
            raise click.ClickException(e)

    return wrapper

@click.command()
@click.option("--count", default=1, help="Number of greetings.")
@catch_exception(handle=(NumberTooLarge, ValueError))
def hello(count):
    """Simple program that greets NAME for a total of COUNT times."""
    if count > 100:
        raise NumberTooLarge('count cannot be greater than 100')
    if count < 0:
        raise ValueError('count too small')
    click.echo('Great choice!')


if __name__ == "__main__":
    hello()

Другие вопросы по тегам