使用Click向组添加通用参数

ora*_*nge 5 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={})
Run Code Online (Sandbox Code Playgroud)

这是使用command2时的结果:

$ python cli-test.py command2 --optparam=123
> Error: no such option: --optparam`
Run Code Online (Sandbox Code Playgroud)

这个例子有什么问题。我试图密切关注文档,但opt-param似乎没有得到认可。

Ste*_*uch 5

所需方案的基本问题是click.CommandCollection不调用组函数。它直接跳到命令。另外,希望通过装饰器将选项应用到组,但需要通过命令来解析选项。那是:

> my_prog my_command --group-option
Run Code Online (Sandbox Code Playgroud)

代替:

> my_prog --group-option my_command
Run Code Online (Sandbox Code Playgroud)

怎么样?

click.Group派生类将命令调用挂接到命令,以拦截组参数,并将其传递给group命令。

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

码:

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
Run Code Online (Sandbox Code Playgroud)

测试代码:

@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())
Run Code Online (Sandbox Code Playgroud)

结果:

command2a foo:from group2 OP None
Run Code Online (Sandbox Code Playgroud)