使用 Click 将常用参数添加到组
Adding common parameters to groups with Click
我正在尝试使用 Python 库 Click,但很难让示例正常工作。我定义了两组,其中一组 (group2
) 用于处理这组命令的公共参数。我想要实现的是,那些公共参数由组函数 (group2
) 处理并分配给上下文变量,因此它们可以被实际命令使用。
一个用例是一些需要用户名和密码的命令,而其他一些则不需要(甚至不是可选的)。
这是代码
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 时的结果:
$ python cli-test.py command2 --optparam=123
> Error: no such option: --optparam`
这个例子有什么问题。我试图密切关注文档,但 opt-param
似乎无法识别。
这不是我要寻找的答案,而是朝着它迈出的一步。本质上引入了一种新的组 (GroupExt
),添加到组中的选项现在被添加到命令中。
$ python cli-test.py command2 --optparam=12
cli
command2 12
import click
class GroupExt(click.Group):
def add_command(self, cmd, name=None):
click.Group.add_command(self, cmd, name=name)
for param in self.params:
cmd.params.append(param)
@click.group()
def group1():
pass
@group1.command()
@click.argument('argument1')
@click.option('--option1')
def command1(argument1, option1):
print 'In command2', argument1, option1
# Equivalent to @click.group() with special group
@click.command(cls=GroupExt)
@click.option('--optparam', default=None, type=str)
def group2():
print 'in group2'
@group2.command()
def command2(optparam):
print 'command2', optparam
@click.command(cls=click.CommandCollection, sources=[group1, group2])
def cli():
print 'cli'
if __name__ == '__main__':
cli(obj={})
这不是我要找的。理想情况下,optparam
将由 group2
处理并将结果放入上下文中,但目前它在 command2
中处理。也许有人知道如何扩展它。
所需方案的基本问题是 click.CommandCollection
不调用组函数。它直接跳到命令。此外,希望通过装饰器将选项应用于组,但让命令解析选项。即:
> my_prog my_command --group-option
而不是:
> my_prog --group-option my_command
如何?
这个click.Group
派生的class挂钩命令调用拦截组参数,并将它们传递给组命令。
- 在
Group.add_command
中,将参数添加到命令
- 在
Group.add_command
中,覆盖 command.invoke
- 在覆盖
command.invoke
中,从组中取出特殊参数并将它们放入 ctx.obj
并从 params
中删除它们
- 在覆盖
command.invoke
中,调用组命令,然后调用命令本身
代码:
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
我正在尝试使用 Python 库 Click,但很难让示例正常工作。我定义了两组,其中一组 (group2
) 用于处理这组命令的公共参数。我想要实现的是,那些公共参数由组函数 (group2
) 处理并分配给上下文变量,因此它们可以被实际命令使用。
一个用例是一些需要用户名和密码的命令,而其他一些则不需要(甚至不是可选的)。
这是代码
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 时的结果:
$ python cli-test.py command2 --optparam=123
> Error: no such option: --optparam`
这个例子有什么问题。我试图密切关注文档,但 opt-param
似乎无法识别。
这不是我要寻找的答案,而是朝着它迈出的一步。本质上引入了一种新的组 (GroupExt
),添加到组中的选项现在被添加到命令中。
$ python cli-test.py command2 --optparam=12
cli
command2 12
import click
class GroupExt(click.Group):
def add_command(self, cmd, name=None):
click.Group.add_command(self, cmd, name=name)
for param in self.params:
cmd.params.append(param)
@click.group()
def group1():
pass
@group1.command()
@click.argument('argument1')
@click.option('--option1')
def command1(argument1, option1):
print 'In command2', argument1, option1
# Equivalent to @click.group() with special group
@click.command(cls=GroupExt)
@click.option('--optparam', default=None, type=str)
def group2():
print 'in group2'
@group2.command()
def command2(optparam):
print 'command2', optparam
@click.command(cls=click.CommandCollection, sources=[group1, group2])
def cli():
print 'cli'
if __name__ == '__main__':
cli(obj={})
这不是我要找的。理想情况下,optparam
将由 group2
处理并将结果放入上下文中,但目前它在 command2
中处理。也许有人知道如何扩展它。
所需方案的基本问题是 click.CommandCollection
不调用组函数。它直接跳到命令。此外,希望通过装饰器将选项应用于组,但让命令解析选项。即:
> my_prog my_command --group-option
而不是:
> my_prog --group-option my_command
如何?
这个click.Group
派生的class挂钩命令调用拦截组参数,并将它们传递给组命令。
- 在
Group.add_command
中,将参数添加到命令 - 在
Group.add_command
中,覆盖command.invoke
- 在覆盖
command.invoke
中,从组中取出特殊参数并将它们放入ctx.obj
并从params
中删除它们
- 在覆盖
command.invoke
中,调用组命令,然后调用命令本身
代码:
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