所需方案的基本问题是click.CommandCollection
不调用群组功能。它直接跳到命令。此外,希望通过装饰器将选项应用于组,但由命令解析选项。那是:
> my_prog my_command --group-option
代替:
> my_prog --group-option my_command
How?
This click.Group
派生类挂钩命令的命令调用以拦截组参数,并将它们传递给组命令。
- In
Group.add_command
,将参数添加到命令中
- In
Group.add_command
, 覆盖command.invoke
- 在被覆盖的情况下
command.invoke
,从组中取出特殊参数并将它们放入ctx.obj
并将它们从params
- 在被覆盖的情况下
command.invoke
,调用组命令,然后调用命令本身
Code:
import click
class GroupWithCommandOptions(click.Group):
""" Allow application of options to group with multi command """
def add_command(self, cmd, name=None):
click.Group.add_command(self, cmd, name=name)
# add the group parameters to the command
for param in self.params:
cmd.params.append(param)
# hook the commands invoke with our own
cmd.invoke = self.build_command_invoke(cmd.invoke)
self.invoke_without_command = True
def build_command_invoke(self, original_invoke):
def command_invoke(ctx):
""" insert invocation of group function """
# separate the group parameters
ctx.obj = dict(_params=dict())
for param in self.params:
name = param.name
ctx.obj['_params'][name] = ctx.params[name]
del ctx.params[name]
# call the group function with its parameters
params = ctx.params
ctx.params = ctx.obj['_params']
self.invoke(ctx)
ctx.params = params
# now call the original invoke (the command)
original_invoke(ctx)
return command_invoke
测试代码:
@click.group()
@click.pass_context
def group1(ctx):
pass
@group1.command()
@click.argument('argument1')
@click.option('--option1')
def command1(argument1, option1):
click.echo('In command2 %s %s' % (argument1, option1))
@click.group(cls=GroupWithCommandOptions)
@click.option('--optparam', default=None, type=str)
@click.option('--optparam2', default=None, type=str)
@click.pass_context
def group2(ctx, optparam, optparam2):
# create_foo_by_processing_params(optparam, optparam2)
ctx.obj['foo'] = 'from group2 %s %s' % (optparam, optparam2)
@group2.command()
@click.pass_context
def command2a(ctx):
click.echo('command2a foo:%s' % ctx.obj['foo'])
@group2.command()
@click.option('--another-param', default=None, type=str)
@click.pass_context
def command2b(ctx, another_param):
click.echo('command2b %s %s' % (ctx['foo'], another_param))
cli = click.CommandCollection(sources=[group1, group2])
if __name__ == '__main__':
cli('command2a --optparam OP'.split())
Results:
command2a foo:from group2 OP None