一串命令的别名

时间:2017-12-26 20:22:02

标签: python command-line-interface python-click

我有tool个命令:step1step2step3

我可以通过调用来链接它们:

$ tool step1 step2 step3

我希望有一个名为all的别名来通过调用:

来运行所有步骤

$ tool all

我找到了一个有效的解决方案,但由于在引擎盖下两次调用cli(),它似乎不适合我:

@click.group(chain=True)
def cli():
    print('cli() has been called')

...

@cli.command()
def all():
    cli(args=['step1', 'step2', 'step3'])

如果没有两次调用cli()的副作用,怎么办呢?

1 个答案:

答案 0 :(得分:8)

提供一些别名的一种方法是拦截命令并直接操作args列表。这可以通过以下自定义类来完成:

自定义类

此类重写click.Group.__call__()方法以允许在调用命令处理器之前编辑args列表。此外,它会覆盖format_epilog以添加别名的帮助文档。

class ExpandAliasesGroup(click.Group):

    def __init__(self, *args, **kwargs):
        self.aliases = kwargs.pop('aliases', {})
        super(ExpandAliasesGroup, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if args[0][0] in self.aliases:
            alias = self.aliases[args[0][0]]
            args[0].pop(0)
            for command in reversed(alias):
                args[0].insert(0, command)
        return super(ExpandAliasesGroup, self).__call__(*args, **kwargs)

    @property
    def alias_help(self):
        return '\n'.join(
            '{}:  {}'.format(alias, ' '.join(commands))
            for alias, commands in sorted(self.aliases.items())
        )

    def format_epilog(self, ctx, formatter):
        """Inject our aliases into the help string"""

        if self.aliases:
            formatter.write_paragraph()
            formatter.write_text('Aliases:')
            with formatter.indentation():
                formatter.write_text(self.alias_help)

        # call the original epilog
        super(ExpandAliasesGroup, self).format_epilog(ctx, formatter)

使用自定义类

通过将cls参数和别名的dict传递给click.group()装饰器,ExpandAliasesGroup类可以进行别名扩展。

aliases = dict(all='command1 command2 command3'.split())

@click.group(chain=True, cls=ExpandAliasesGroup, aliases=aliases)
def cli():
    ....

这是如何工作的?

这是有效的,因为click是一个设计良好的OO框架。 @click.group()装饰器通常实例化click.Group对象,但允许使用cls参数覆盖此行为。因此,在我们自己的班级继承click.Group并过度使用所需的方法是一件相对容易的事。

通过覆盖__call__方法,我们可以拦截所有命令调用。然后,如果args列表以一个已知的别名开头,我们通过删除该别名命令并用别名替换它来编辑args列表。

通过覆盖format_epilog方法,我们可以添加别名的帮助文档。

测试代码:

import click

aliases = dict(all='command1 command2 command3'.split())

@click.group(cls=ExpandAliasesGroup, chain=True, aliases=aliases)
def cli():
    pass

@cli.command()
def command1():
    click.echo('Command 1')

@cli.command()
def command2():
    click.echo('Command 2')

@cli.command()
def command3():
    click.echo('Command 3')

if __name__ == "__main__":
    commands = (
        'command1',
        'command3',
        'command1 command2',
        'all',
        '--help',
    )

    for cmd in commands:
        try:
            print('-----------')
            print('> ' + cmd)
            cli(cmd.split())
        except:
            pass        

测试结果:

-----------
> command1
Command 1
-----------
> command3
Command 3
-----------
> command1 command2
Command 1
Command 2
-----------
> all
Command 1
Command 2
Command 3
-----------
> --help
Usage: test.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  --help  Show this message and exit.

Commands:
  command1  Command #1 comes first
  command2  Command #2 is after command #1
  command3  Command #3 saves the best for last

Aliases:
  all:  command1 command2 command3