使用Python“click”CLI,如何向子命令添加公共选项,这些子命令可以在*子命令的名称之后*

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

使用CLI库click我有一个带有两个子命令app.pyread的应用程序脚本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。我知道我可以将它作为选项添加到命令组中

@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个回答
3
投票

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() 将关键字参数收集为dict。

(或者,您可以编写一个更高级的装饰器,将参数提供给上下文或类似的东西,但我的简单尝试不起作用,我还没准备好尝试更高级的方法。我可能会稍后编辑答案,添加它们。)

有了它,我们可以制作一个程序:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.