使用Cli向组添加公共参数

2024-10-01 13:41:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图使用Python库Click,但是很难让一个示例正常工作。我定义了两个组,其中一个组(group2)用于处理这组命令的公共参数。我想实现的是,这些公共参数由group函数(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时的结果:

^{pr2}$

这个例子有什么问题吗。我试着密切关注文档,但是opt-param似乎没有被识别出来。在


Tags: 命令fooparamdefcontextgrouppasscommand
2条回答

所需方案的基本问题是click.CommandCollection不调用group函数。它直接跳到命令。此外,还需要通过decorator将选项应用于组,但是命令将解析这些选项。即:

> my_prog my_command  group-option

而不是:

^{pr2}$

怎么做的?

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

  1. Group.add_command中,将参数添加到命令中
  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

测试代码:

@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

这不是我要找的答案,而是朝着这个方向迈出的一步。本质上引入了一种新的组(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处理并将结果放入上下文中,但目前它是在{}中处理的。也许有人知道如何扩展这一点。在

相关问题 更多 >