python - 命令链的别名

标签 python command-line-interface python-click

我有一个带有命令的工具:step1step2step3

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

$工具step1 step2 step3

我想要一个名为 all 的别名来运行所有步骤,方法是调用:

$工具全部

我找到了一个可行的解决方案,但它似乎不适合我,因为在后台调用了两次 cli():

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

...

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

在没有调用 cli() 两次的副作用的情况下,如何做到这一点?

最佳答案

提供一些别名的一种方法是拦截命令并直接操作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 and args[0] and 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 参数和别名字典传递给 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

关于python - 命令链的别名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47983367/

相关文章:

Python 2 中的 Python 3 f-string 替代品

python - Oracle 中的 SQLAlchemy 大数截断/舍入问题

python - Matplotlib 分配给子图的颜色条错误

amazon-web-services - 使用 AWS CLI 将单引号转换为特殊字符

python - 如何在Python中创建一个可以通过PIP安装的CLI?

php - 让 pdo_mysql 驱动程序在 CLI 中工作

python - 使用 Python Click 测试参数

python - re.sub 错误与 "Expected string or bytes-like object"

python - 点击在 jupyter 中不起作用

python - 单击命令行界面 : Make options required if other optional option is unset