使用Click将常用参数添加到组中 [英] Adding common parameters to groups with Click
问题描述
我正在尝试使用Python库点击,但很难使示例工作.我定义了两组,其中一组(group2
)用于处理该组命令的公共参数.我要实现的是那些通用参数由组函数(group2
)处理并分配给上下文变量,以便它们可以由实际命令使用.
I am trying to use the Python library Click, but struggle to get an example working. I defined two groups, one of which (group2
) is meant to handle common parameters for this group of commands. What I want to achieve is that those common parameters get processed by the group function (group2
) and assigned to the context variable, so they can be used by the actual commands.
一个用例是许多需要用户名和密码的命令,而有些则不需要(甚至是可选的).
A use case would be a number of commands that require username and password, while some others don't (not even optionally).
这是代码
import click
@click.group()
@click.pass_context
def group1(ctx):
pass
@click.group()
@click.option('--optparam', default=None, type=str)
@click.option('--optparam2', default=None, type=str)
@click.pass_context
def group2(ctx, optparam):
print 'in group2', optparam
ctx['foo'] = create_foo_by_processing_params(optparam, optparam2)
@group2.command()
@click.pass_context
def command2a(ctx):
print 'command2a', ctx['foo']
@group2.command()
@click.option('--another-param', default=None, type=str)
@click.pass_context
def command2b(ctx, another_param):
print 'command2b', ctx['foo'], another_param
# many more more commands here...
# @group2.command()
# def command2x():
# ...
@group1.command()
@click.argument('argument1')
@click.option('--option1')
def command1(argument1, option1):
print 'In command2', argument1, option1
cli = click.CommandCollection(sources=[group1, group2])
if __name__ == '__main__':
cli(obj={})
这是使用command2时的结果:
And this is the result when using command2:
$ python cli-test.py command2 --optparam=123
> Error: no such option: --optparam`
此示例出了什么问题.我试图密切关注文档,但是opt-param
似乎未被认可.
What's wrong with this example. I tried to follow the docs closely, but opt-param
doesn't seem to be recognised.
推荐答案
所需方案的基本问题是click.CommandCollection
不调用组函数.它直接跳到命令.另外,希望通过装饰器将选项应用到组,但是需要通过命令来解析选项.那就是:
The basic issue with the desired scheme is that click.CommandCollection
does not call the group function. It skips directly to the command. In addition it is desired to apply options to the group via decorator, but have the options parsed by the command. That is:
> my_prog my_command --group-option
代替:
> my_prog --group-option my_command
如何?
此click.Group
派生类挂钩命令调用,以拦截命令并将组参数传递给group命令.
How?
This click.Group
derived class hooks the command invocation for the commands to intercept the group parameters, and pass them to the group command.
- 在
Group.add_command
中,将参数添加到命令中 - 在
Group.add_command
中,覆盖command.invoke
- 在已覆盖的
command.invoke
中,将从组中插入的特殊args放入ctx.obj
并将其从params
中删除 - 在已覆盖的
command.invoke
中,先调用group命令,然后再调用命令本身
- In
Group.add_command
, add the params to the command - In
Group.add_command
, overridecommand.invoke
- In overridden
command.invoke
, take the special args inserted from the group and put them intoctx.obj
and remove them fromparams
- In overridden
command.invoke
, invoke the group command, and then the command itself
代码:
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())
结果:
command2a foo:from group2 OP None
这篇关于使用Click将常用参数添加到组中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!