Могу ли я иметь функцию main () Click, которая вызывает все остальные подкоманды? - PullRequest
0 голосов
/ 16 мая 2018

У меня есть скрипт вроде:

#myscript.py
import click

def step1(arg1):
    print('Step 1: ' + arg1)

def step2(arg2):
    print('Step 2: ' + arg2)

def main(arg1, arg2):
    step1(arg1)
    step2(arg2)

Большую часть времени Я хочу запустить скрипт с myscript arg1 arg2, но иногда мне может потребоваться выполнить только один шаг:например, myscript step1 arg1. Как мне настроить щелчок, чтобы сделать это? Есть ли способ иметь одну команду по умолчанию, а затем другие необязательные команды?

Кажется, это одна вещь что Click не поощряет :

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

Нужно ли использовать этот паттерн click.invoke()?

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

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

Мое решение было что-то вроде:

@click.group(invoke_without_command=True)
@click.option('--arg1')
@click.option('--arg2')
@click.pass_context
def cli(ctx, arg1, arg2):
    '''Description
    '''
    if ctx.invoked_subcommand is None:
        do_everything(ctx, arg1, arg2)

@cli.command()
@click.option('--arg1')
def step_1(arg1):
    return do_something(arg1)

@cli.command()
@click.argument('step_one_result')
@click.option('--arg2')
def step_2(step_one_result, arg2):
    do_something_else(step_one_result, arg2)

def do_everything(ctx, arg1, arg2):
    step_one_result = ctx.invoke(step_1, arg1=arg1)
    ctx.invoke(do_something_else, step_one_result=step_one_result, arg2=arg2)

#and because of weirdness with pass_context and using setuptools

def main():
    cli(obj={})

if __name__ == '__main__':
    main()

РЕДАКТИРОВАТЬ: вы заметите использование ctx.invoke(), которое было необходимо для вызова функций без получения следующей ошибки

line 619, in make_context
    ctx = Context(self, info_name=info_name, parent=parent, **extra)
TypeError: __init__() got an unexpected keyword argument 'arg1'
0 голосов
/ 16 мая 2018

Я думаю, что функции Multi Command Chaining и Multi Command Pipelines предназначены для решения этой ситуации.Конвейерная передача дает точное запрошенное поведение (и step1, и step2, вызываемые, когда ничего не указано в командной строке), но это более многословно, и с chain=True ни один из аргументов не может быть необязательным;вы должны либо (а) всегда давать arg1 и arg2, даже когда вызываете только step2;или (b) преобразовать эти аргументы в параметры (--arg1 foo вместо foo).

Цепочка

import click

@click.group(chain=True)
def cli():
    pass

@cli.command()
@click.argument('arg1')
def step1(arg1):
    click.echo('Step 1: ' + arg1)

@cli.command()
@click.argument('arg2')
def step2(arg2):
    click.echo('Step 2: ' + arg2)

cli()

Затем:

$ python3 chain.py step1 foo step2 bar
Step 1: foo
Step 2: bar
$ python3 chain.py step2 bar
Step 2: bar

Конвейер

import click

@click.group(chain=True, invoke_without_command=True)
@click.argument('arg1')
@click.argument('arg2')
def cli(arg1, arg2):
    pass

@cli.resultcallback()
def process_pipeline(processors, **kwargs):
    # If no commands given, invoke step1 then step2
    processors = processors if len(processors) else [step1, step2]
    for processor in processors:
        processor(**kwargs)

def step1(**kwargs):
    click.echo('Step 1: ' + kwargs['arg1'])

def step2(**kwargs):
    click.echo('Step 2: ' + kwargs['arg2'])

@cli.command('step1')
def make_step1():
    return step1

@cli.command('step2')
def make_step2():
    return step2

cli()

Тогда

$ python3 pipeline.py foo bar
Step 1: foo
Step 2: bar
$ python3 pipeline.py foo bar step2
Step 2: bar
...