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

hal*_*leo 5 python options command-line-interface

使用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")
Run Code Online (Sandbox Code Playgroud)

我想声明一个通用选项--format。我知道我可以将其添加为一个选项,命令通过

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

但是然后我无法在命令给出选项,在我的用例中,这自然得多。我希望能够在外壳中发出:

app.py read --format XXX 
Run Code Online (Sandbox Code Playgroud)

但是有了概述的设置,我得到了消息Error: no such option: --format。该脚本仅接受命令的选项。

所以我的问题是:如何在两个子命令中添加一个通用选项,以使其像给每个子命令提供该选项一样起作用?

mat*_*cik 7

AFAICT,这无法通过Click实现。该文档指出:

单击严格将命令和子命令之间的参数分开。这意味着必须在命令名称本身之后但在任何其他命令名称之前指定特定命令的选项和参数。

可能的解决方法是编写common_options装饰器。下面的示例使用的事实click.option是一个函数,该函数返回一个预期要串联应用的装饰器函数。IOW,以下内容:

@click.option("-a")
@click.option("-b")
def hello(a, b):
    pass
Run Code Online (Sandbox Code Playgroud)

等效于以下内容:

def hello(a, b):
    pass

hello = click.option("-a")(click.option("-b")(hello))
Run Code Online (Sandbox Code Playgroud)

缺点是您需要在所有子命令上设置通用参数。可以通过解决此问题**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()
Run Code Online (Sandbox Code Playgroud)

  • +1 好答案!维护者确认他们不会添加此内容,但 GitHub 问题中有类似的解决方法:https://github.com/pallets/click/issues/108#issuecomment-194465429 (4认同)