Используя Python `click` CLI, как добавить общие опции к подкомандам, которые могут идти * после * имени подкоманды - PullRequest
0 голосов
/ 03 сентября 2018

Использование библиотеки CLI click У меня есть скрипт приложения app.py с двумя подкомандами read и write:

@click.group()
@click.pass_context
def cli(ctx):
    pass

@cli.command()
@click.pass_context
def read(ctx):
    print("read")

@cli.command()
@click.pass_context
def write(ctx):
    print("write")

Я хочу объявить общую опцию --format. Я знаю, что могу добавить его в качестве опции к команде group via

@click.group()
@click.option('--format', default='json')
@click.pass_context
def cli(ctx, format):
    ctx.obj['format'] = format

Но тогда я не могу дать опции после команду, что в моем случае использования намного более естественно. Я хочу иметь возможность выдавать в оболочке:

app.py read --format XXX 

но с указанными настройками я получаю сообщение Error: no such option: --format. Сценарий принимает только опцию перед командой.

Итак, мой вопрос: как я могу добавить общий параметр для обеих подкоманд, чтобы он работал так, как если бы этот параметр был задан для каждой подкоманды?

1 Ответ

0 голосов
/ 03 сентября 2018

AFAICT, это невозможно с Click. Документы утверждают, что:

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

Возможный обходной путь - написание common_options декоратора. В следующем примере используется тот факт, что click.option - это функция, которая возвращает функцию декоратора, которая должна быть применена последовательно. IOW, следующее:

@click.option("-a")
@click.option("-b")
def hello(a, b):
    pass

эквивалентно следующему:

def hello(a, b):
    pass

hello = click.option("-a")(click.option("-b")(hello))

Недостатком является то, что вам нужно установить общий аргумент для всех ваших подкоманд. Это может быть решено с помощью **kwargs, который собирает аргументы ключевых слов в виде слова.

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

С этим мы можем сделать программу:

import click
import functools

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

def common_options(f):
    options = [
        click.option("-a", is_flag=True),
        click.option("-b", is_flag=True),
    ]
    return functools.reduce(lambda x, opt: opt(x), options, f)

@cli.command()
@common_options
def hello(**kwargs):
    print(kwargs)
    # to get the value of b:
    print(kwargs["b"])

@cli.command()
@common_options
@click.option("-c", "--citrus")
def world(citrus, a, **kwargs):
    print("citrus is", citrus)
    if a:
        print(kwargs)
    else:
        print("a was not passed")

if __name__ == "__main__":
    cli()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...