使用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
. 我知道我可以把它作为一个选项加入到命令中去 群体 通过
@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
. 脚本只接受选项 之前 的命令。
所以我的问题是:如何给这两个子命令添加一个共同的选项,使其工作起来就像给每个子命令都添加了选项一样?
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()