10

我正在尝试使用 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似乎没有得到认可。

4

2 回答 2

8

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

> my_prog my_command --group-option

代替:

> my_prog --group-option my_command

如何?

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

  1. Group.add_command中,将参数添加到命令中
  2. Group.add_command, 覆盖command.invoke
  3. 在 overriddencommand.invoke中,从组中取出特殊的 args 并将它们放入ctx.obj并从中删除params
  4. 在 overriddencommand.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

测试代码:

@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
于 2017-05-28T16:51:04.370 回答
1

这不是我正在寻找的答案,而是朝着它迈出的一步。本质上,引入了一种新的组 ( 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. 也许有人知道如何扩展它。

于 2017-05-27T12:18:44.047 回答