命令之间的共享选项和标志
Shared options and flags between commands
假设我的 CLI 实用程序有三个命令:cmd1
、cmd2
、cmd3
我希望 cmd3
具有与 cmd1
和 cmd2
相同的选项和标志。就像某种继承。
@click.command()
@click.options("--verbose")
def cmd1():
pass
@click.command()
@click.options("--directory")
def cmd2():
pass
@click.command()
@click.inherit(cmd1, cmd2) # HYPOTHETICAL
def cmd3():
pass
所以 cmd3
将有标志 --verbose
和选项 --directory
。可以用 Click 实现吗?也许我只是忽略了文档中的某些内容...
编辑: 我知道我可以用 click.group()
做到这一点。但是所有组的选项必须在组的命令之前指定。我想在命令后正常拥有所有选项。
cli.py --verbose --directory /tmp cmd3
-> cli.py cmd3 --verbose --directory /tmp
此代码从其参数中提取所有选项
def extract_params(*args):
from click import Command
if len(args) == 0:
return ['']
if any([ not isinstance(a, Command) for a in args ]):
raise TypeError('Handles only Command instances')
params = [ p.opts() for cmd_inst in args for p in cmd_inst.params ]
return list(set(params))
现在可以使用了:
@click.command()
@click.option(extract_params(cmd1, cmd2))
def cmd3():
pass
这段代码只提取了参数和none它们的默认值,如果需要可以改进。
我找到了一个简单的解决方案!我稍微编辑了 https://github.com/pallets/click/issues/108 中的片段:
import click
_cmd1_options = [
click.option('--cmd1-opt')
]
_cmd2_options = [
click.option('--cmd2-opt')
]
def add_options(options):
def _add_options(func):
for option in reversed(options):
func = option(func)
return func
return _add_options
@click.group()
def group(**kwargs):
pass
@group.command()
@add_options(_cmd1_options)
def cmd1(**kwargs):
print(kwargs)
@group.command()
@add_options(_cmd2_options)
def cmd2(**kwargs):
print(kwargs)
@group.command()
@add_options(_cmd1_options)
@add_options(_cmd2_options)
@click.option("--cmd3-opt")
def cmd3(**kwargs):
print(kwargs)
if __name__ == '__main__':
group()
用通用参数定义一个class
class StdCommand(click.core.Command):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.params.insert(0, click.core.Option(('--default-option',), help='Every command should have one'))
然后在定义命令函数时将class传给装饰器
@click.command(cls=StdCommand)
@click.option('--other')
def main(default_option, other):
...
您还可以为共享选项设置另一个装饰器。我找到了这个解决方案 here
def common_params(func):
@click.option('--foo')
@click.option('--bar')
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@click.command()
@common_params
@click.option('--baz')
def cli(foo, bar, baz):
print(foo, bar, baz)
假设我的 CLI 实用程序有三个命令:cmd1
、cmd2
、cmd3
我希望 cmd3
具有与 cmd1
和 cmd2
相同的选项和标志。就像某种继承。
@click.command()
@click.options("--verbose")
def cmd1():
pass
@click.command()
@click.options("--directory")
def cmd2():
pass
@click.command()
@click.inherit(cmd1, cmd2) # HYPOTHETICAL
def cmd3():
pass
所以 cmd3
将有标志 --verbose
和选项 --directory
。可以用 Click 实现吗?也许我只是忽略了文档中的某些内容...
编辑: 我知道我可以用 click.group()
做到这一点。但是所有组的选项必须在组的命令之前指定。我想在命令后正常拥有所有选项。
cli.py --verbose --directory /tmp cmd3
-> cli.py cmd3 --verbose --directory /tmp
此代码从其参数中提取所有选项
def extract_params(*args):
from click import Command
if len(args) == 0:
return ['']
if any([ not isinstance(a, Command) for a in args ]):
raise TypeError('Handles only Command instances')
params = [ p.opts() for cmd_inst in args for p in cmd_inst.params ]
return list(set(params))
现在可以使用了:
@click.command()
@click.option(extract_params(cmd1, cmd2))
def cmd3():
pass
这段代码只提取了参数和none它们的默认值,如果需要可以改进。
我找到了一个简单的解决方案!我稍微编辑了 https://github.com/pallets/click/issues/108 中的片段:
import click
_cmd1_options = [
click.option('--cmd1-opt')
]
_cmd2_options = [
click.option('--cmd2-opt')
]
def add_options(options):
def _add_options(func):
for option in reversed(options):
func = option(func)
return func
return _add_options
@click.group()
def group(**kwargs):
pass
@group.command()
@add_options(_cmd1_options)
def cmd1(**kwargs):
print(kwargs)
@group.command()
@add_options(_cmd2_options)
def cmd2(**kwargs):
print(kwargs)
@group.command()
@add_options(_cmd1_options)
@add_options(_cmd2_options)
@click.option("--cmd3-opt")
def cmd3(**kwargs):
print(kwargs)
if __name__ == '__main__':
group()
用通用参数定义一个class
class StdCommand(click.core.Command):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.params.insert(0, click.core.Option(('--default-option',), help='Every command should have one'))
然后在定义命令函数时将class传给装饰器
@click.command(cls=StdCommand)
@click.option('--other')
def main(default_option, other):
...
您还可以为共享选项设置另一个装饰器。我找到了这个解决方案 here
def common_params(func):
@click.option('--foo')
@click.option('--bar')
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@click.command()
@common_params
@click.option('--baz')
def cli(foo, bar, baz):
print(foo, bar, baz)