如何在子命令中添加常用选项,这些选项可以放在子命令名称的*后。

问题描述 投票:7回答:1

使用CLI库 click 我有一个应用程序脚本 app.py 与两个子命令 readwrite:

@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. 我知道我可以把它作为一个选项加入到命令中去 群体 通过

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

但是,我不能给选择 之后 命令,而在我的使用中,这是更自然的。我希望能够在shell中发布。

app.py read --format XXX 

但在概述的设置下,我得到的信息是: Error: no such option: --format. 脚本只接受选项 之前 的命令。

所以我的问题是:如何给这两个子命令添加一个共同的选项,使其工作起来就像给每个子命令都添加了选项一样?

python command-line-interface options
1个回答
8
投票

AFAICT,这在Click中是不可能的。文档中说。

Click 严格区分了命令和子命令的参数。 这意味着特定命令的选项和参数必须在命令名之后,而不是在其他命令名之前指定。

一个可能的变通方法是写一个 common_options 装饰者。下面的例子是利用 click.option 是一个函数,返回一个期望串联应用的装饰函数。即,下面的。

@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,它将关键字参数收集为一个dict。

(或者,你可以写一个更高级的装饰器,将参数反馈到上下文中或者类似的东西,但我的简单尝试没有成功,我还没有准备好尝试更高级的方法。我可能会在以后编辑答案并添加它们)。

有了这些,我们就可以做一个程序了。

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()
© www.soinside.com 2019 - 2024. All rights reserved.