带有常用选项的 Python 多命令 CLI [英] Python multi-command CLI with common options

查看:25
本文介绍了带有常用选项的 Python 多命令 CLI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的 Python 应用程序添加 CLI.CLI 应该允许一次运行多个命令.命令应该有通用选项和个人选项.

I am adding CLI for my Python application. The CLI should allow to run multiple commands in a time. The commands should have common options and personal options.

示例:

$ python mycliapp.py --common-option1 value1 --common-option2 value2 cmd1 --cmd1-option cmd2 --cmd2-option somevalue cmd3

该示例有两个所有命令都使用的通用选项,每个命令可以有或没有该命令仅使用的选项.

The example has two common options used by all commands and each command can have or not the option used by the command only.

我考虑过 Python Click.它具有丰富的功能,但它不允许(至少我没有发现)在没有一些主要命令的情况下使用通用选项.

I have considered Python Click. It has rich functionality, but it does not allow (at least I didn't found) to use common options without some main command.

使用 Click 时,上面的示例将如下所示:

$ python mycliapp.py maincmd --common-option1 value1 --common-option2 value2 cmd1 --cmd1-option cmd2 --cmd2-option somevalue cmd3

此外,还考虑了 Python Argparse.看起来它可以做我需要的事情,我已经设法编写了一个代码,它可以使用通用选项和单个命令,但无法使用多个命令.这个页面 Python argparse - Add argument to multiple subparsers 有很好的例子,但似乎 command2 应该是 command1 的子命令.这有点不同,因为我需要可以按任何顺序执行命令.

Also, considered Python Argparse. It looks that it can do what I need and I have managed to write a code, which works with common options and single command, but cannot manage to use multiple commands. This page Python argparse - Add argument to multiple subparsers has good example, but seems that command2 should be a sub-command of command1. It is a bit different since I need that the commands can be executed in any order.

推荐答案

点击 绝对支持这种语法.一个简单的例子看起来像:

Click absolutely supports this sort of syntax. A simple example looks something like:

import click


@click.group(chain=True)
@click.option('--common-option1')
@click.option('--common-option2')
def main(common_option1, common_option2):
    pass


@main.command()
@click.option('--cmd1-option', is_flag=True)
def cmd1(cmd1_option):
    pass


@main.command()
@click.option('--cmd2-option')
def cmd2(cmd2_option):
    pass


@main.command()
def cmd3():
    pass


if __name__ == '__main__':
    main()

假设上面在mycliapp.py中,我们看到了常见的帮助输出:

Assuming the above is in mycliapp.py, we see the common help output:

$ python example.py --help
Usage: example.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  --common-option1 TEXT
  --common-option2 TEXT
  --help                 Show this message and exit.

Commands:
  cmd1
  cmd2
  cmd3

对于cmd1:

$ python mycliapp.py cmd1 --help
Usage: mycliapp.py cmd1 [OPTIONS]

Options:
  --cmd1-option
  --help         Show this message and exit.

对于cmd2:

$ python mycliapp.py cmd2 --help
Usage: mycliapp.py cmd2 [OPTIONS]

Options:
  --cmd2-option TEXT
  --help              Show this message and exit.

有了这个,我们可以从您的问题运行命令行:

With this we can run the command line from your question:

python mycliapp.py --common-option1 value1 --common-option2 value2 \
  cmd1 --cmd1-option \
  cmd2 --cmd2-option somevalue \
  cmd3


更新 1

这是使用建议的回调模型实现管道的示例 在文档中:

Here's an example that implements pipelines using the callback model suggested in the documentation:

import click


@click.group(chain=True)
@click.option('--common-option1')
@click.option('--common-option2')
@click.pass_context
def main(ctx, common_option1, common_option2):
    ctx.obj = {
        'common_option1': common_option1,
        'common_option2': common_option2,
    }


@main.resultcallback()
def process_pipeline(processors, common_option1, common_option2):
    print('common_option1 is', common_option1)
    for func in processors:
        res = func()
        if not res:
            raise click.ClickException('Failed processing!')


@main.command()
@click.option('--cmd1-option', is_flag=True)
def cmd1(cmd1_option):
    def process():
        print('This is cmd1')
        return cmd1_option

    return process


@main.command()
@click.option('--cmd2-option')
def cmd2(cmd2_option):
    def process():
        print('This is cmd2')
        return cmd2_option != 'fail'

    return process


@main.command()
@click.pass_context
def cmd3(ctx):
    def process():
        print('This is cmd3 (common option 1 is: {common_option1}'.format(**ctx.obj))
        return True

    return process


if __name__ == '__main__':
    main()

每个命令返回一个布尔值,指示它是否成功.失败的命令将中止管道处理.例如,这里 cmd1 失败,所以 cmd2 永远不会执行:

Each command returns a boolean indicating whether or not it was successful. A failed command will abort pipeline processing. For example, here cmd1 fails so cmd2 never executes:

$ python mycliapp.py cmd1 cmd2
This is cmd1
Error: Failed processing!

但是如果我们让 cmd1 开心,它就会起作用:

But if we make cmd1 happy, it works:

$ python mycliapp.py cmd1 --cmd1-option cmd2
This is cmd1
This is cmd2

同样地,比较一下:

$ python mycliapp.py cmd1 --cmd1-option cmd2 --cmd2-option fail cmd3
This is cmd1
This is cmd2
Error: Failed processing!

有了这个:

$ python mycliapp.py cmd1 --cmd1-option cmd2  cmd3
This is cmd1
This is cmd2
This is cmd3

当然你不需要按顺序调用:

And of course you don't need to call things in order:

$ python mycliapp.py cmd2 cmd1 --cmd1-option
This is cmd2
This is cmd1

这篇关于带有常用选项的 Python 多命令 CLI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆