使用Click向组添加常用参数

时间:2017-05-24 12:20:50

标签: python python-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似乎没有得到认可。

2 个答案:

答案 0 :(得分:2)

所需方案的基本问题是click.CommandCollection不会调用组功能。它直接跳到命令。此外,还希望通过装饰器将选项应用于组,但是具有由命令解析的选项。那就是:

> my_prog my_command --group-option

而不是:

> my_prog --group-option my_command

如何?

click.Group派生类挂钩命令的命令调用,以拦截组参数,并将它们传递给group命令。

  1. Group.add_command中,将参数添加到命令
  2. Group.add_command中,覆盖command.invoke
  3. 在重写command.invoke中,从组中插入特殊参数并将其放入ctx.obj并从params中删除
  4. 在重写command.invoke中,调用group命令,然后调用命令本身
  5. 代码:

    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
    

答案 1 :(得分:0)

这不是我要找的答案,而是迈向它的一步。基本上引入了一种新的组(@IBAction func sortWithName(_ sender: UIButton) { items = allItems.sorted { $0.place < $1.place } self.tableView.reloadData() } @IBAction func sortWithStatus(_ sender: UIButton) { items = allItems.sorted { $0.status < $1.status } self.tableView.reloadData() } @IBAction func sortWithStatusCount(_ sender: UIButton) { //Filter your array first items = allItems.filter { $0.stationsCount != "no data" } //stationsCount is looks like number so sort it using compare with numeric option items.sort { $0.stationsCount.compare($1.stationsCount, options: .numeric) == .orderedAscending } self.tableView.reloadData() } ),添加到组中的选项现在被添加到命令中。

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处理。也许有人知道如何扩展它。