来杯菊花茶

生活不只是眼前的苟且,还有诗和远方

0%

打造高逼格Python命令行应用

python脚本是我等IT脚男日常工作中的必备工具,功能稍微复杂一些的脚本时常需要接受用户输入,根据输入执行相应的分支,如何处理用户输入并提供合适的输出则直接决定了脚本的质量和使用体验,一个合适的命令行框架可以让你的开发和后续使用事半功倍。本文介绍几款开源的python命令行框架,简要对比一下优缺点,希望对你的工作有所帮助。

optparse

optparse 是python内置的命令行工具库之一,它可以提供非交互式命令行框架,即用户输入命令启动python脚本执行完成即退出,不会等待你输入下一个命令,适用于相对简单的python命令行工具,基本用法如下:

代码示例

from optparse import OptionParser

if __name__ == '__main__':
    usage = '''
    usage: -d <domain_name> [-u <username>] -p <password> 
    '''
    parser = OptionParser(usage)

    parser.add_option("-d", "--domain_name", dest="domain_name", help="The domain name of your huawei cloud account.")
    parser.add_option("-u", "--username", dest="username", help="The user name of your huawei cloud account.")
    parser.add_option("-p", "--password", dest="password", help="The password name of your huawei cloud account.")
    (options, args) = parser.parse_args()

    if options.domain_name and options.password:
        # do something
        print("try to do authentication")
    else:
        print("please input your password.")
        print(usage)

执行效果

D:\10_pythonProject\PromptTookit>python optparse_demo/my_app_optparse.py -d demo -p 123
try to do authentication

D:\10_pythonProject\PromptTookit>python optparse_demo/my_app_optparse.py -h
Usage:
    usage: -d <domain_name> [-u <username>] -p <password>


Options:
  -h, --help            show this help message and exit
  -d DOMAIN_NAME, --domain_name=DOMAIN_NAME
                        The domain name of your huawei cloud account.
  -u USERNAME, --username=USERNAME
                        The user name of your huawei cloud account.
  -p PASSWORD, --password=PASSWORD
                        The password name of your huawei cloud account.

框架提供了对命令行参数的解析,help文档支持等常用功能,支持长短命令,可以满足90%的单命令行应用需求,另外因为这个库是python的内置库,所以不需要安装任何依赖也是一个特点。

cmd2

cmd2 是对python的原生库 cmd 的扩展,它提供非常强大的交互式命令行框架,这是它与optparse最大的不同,而同时它除了供命令参数解析、help 文档等必要能力以外,还具备命令补全历史记录嵌套shell 等能力,更重要的是它还可以与argparse配合实现多级命令功能,多级命令在构建复杂的命令行工具的时候是不可或缺的,试想一下如果几十上百个命令参数扁平化显示,那么这个工具操作的复杂性也是难以接受的。

代码示例

import argparse

import cmd2
from cmd2 import with_category

iam_parser = argparse.ArgumentParser()
iam_subparser = iam_parser.add_subparsers(title='iam sub-commands options', help='sub-command help')
iam_info_subparser = iam_subparser.add_parser('user_info', help="query user info by user_id and domain")
iam_info_subparser.add_argument('-u', '--user_id', required=True, help='target user_id')
iam_info_subparser.add_argument('-n', '--domain_name', help='target domain_name, should at least provide domain name or id')


class ApiBox(cmd2.Cmd):
    def __init__(self):
        super().__init__()
        self.prompt = 'api> '
        self.intro = "Documented commands (use 'help -v' for verbose/'help <topic>' for details)"

    def user_info(self, args):
        """query user info"""
        print(f"* query user info for {args.user_id}")

    iam_info_subparser.set_defaults(func=user_info)

    @with_category("Service Commands")
    @cmd2.with_argparser(iam_parser)
    def do_iam(self, args):
        """query iam info"""
        func = getattr(args, 'func', None)
        if func is not None:
            func(self, args)
        else:
            self.do_help('iam')


prompt = ApiBox()
prompt.cmdloop()

执行效果

D:\10_pythonProject\PromptTookit>python cmd2_demo/my_app_cmd2.py
Documented commands (use 'help -v' for verbose/'help <topic>' for details)
api> help

Documented commands (use 'help -v' for verbose/'help <topic>' for details):

Service Commands
================
iam

Uncategorized
=============
alias  help     macro  run_pyscript  set    shortcuts
edit   history  quit   run_script    shell

api> iam user_info
usage: iam user_info [-h] -u USER_ID [-n DOMAIN_NAME]
iam user_info: error: the following arguments are required: -u/--user_id

api> iam user_info -u demo
* query user info for demo
api>

cmd2通过添加装饰器with_category实现help界面的分配显示,通过cmd2.with_argparser配合argsparser类实现二级甚至多级命令,极大程度上扩展了python命令行能够支持的关键字数量,提升用户体验

cmd2支持命令自动补全,凡是do_xxx的命令包括子命令都可以在输入的时候按tab键进行自动补全

另外,cmd2提供了很好的自定义扩展能力,大部分可见的回显内容都可以通过代码重新定义,具体可以参考官方文档

从笔者使用cmd2的体验看,这个库的缺点是,当你的程序需要支持多级命令,同时命令种类又很多的时候,注册命令关键字的代码会变得非常臃肿和难以维护。。。

nubia

python-nubia 是一款特征鲜明的命令行框架,它即支持交互式命令行也支持非交互式命令行,最鲜明的特征是它可以在linux或者windows shell下面用GUI画一个菜单出来,并且命令的输入和输出都支持关键字上色,也支持命令行补全,可以让你的python命令行程序瞬间变得高大上,一图顶万言。

xxx

python底层基于另一个开源项目 python-prompt-toolkit ,前面提到的菜单、上色等特性都是这个库提供的,nubia主要是对它做了二次封装和扩展,以提升易用性

代码示例

nubia也支持多级命令,命令通过command装饰器定义

from nubia import command

@command
class Daemon:
    """
    This is a set of commands that run daemons
    """
    @command
    def start(self) -> None:  # becomes a `daemon start` subcommand
        "Help message of start"
        # Starting the daemon
        ...

    @command
    def stop(self) -> None:  # becomes a `daemon stop` subcommand
        "Help message of stop"
        # Stopping the daemon
        ...

命令参数声明

import typing

@command
@argument("hostnames", description="Hostname for the server you want to start",
    positional=True)
def start_server(hostnames: typing.List[str]):
    """
    Starts a server or more
    """
    pass

官方代码仓也提供了一个更完整的例子,可以参考example文件

从笔者实际使用情况看,这套框架完成度还是很高的,而且通过可视化菜单和命令自动补全的配置确实可以很大程度上提升用户体验,不过一个小小的缺憾是他对windows平台的支撑还是差一点,主要体现在命令着色上,猜测应该是windows使用的色彩库与Linux有所不同,所以windows平台下运行nubia程序有些颜色代码没法正常显示,看起来是这样:

jietu

另外从github上的commit记录以及issue的处理情况来看,这个项目坟头上的草似乎已经长老高了。。。

总结

本文介绍了几个非常实用的python命令行框架,每个框架都有各自分明的优缺点,适用于不同的场景和需求,下面简单罗列一下各自的特点,相信选择正确的框架能让你的工作事半功倍。

optparser cmd2 nubia
交互式命令 不支持 支持 支持
非交互命令 支持 支持 支持
帮助文档 支持 支持 支持
命令补全 不支持 支持 支持
多级命令 不支持 支持 支持
关键字高亮 不支持 不支持 支持
跨平台 支持 支持 支持
活跃度 官方库 活跃 不活跃