2018年03月12日 Mon

给程序添加命令行接口(CLI)的一点体验

程序在实际运行前总会需要配置些外部参数,一般会用固定路径下的文件作为配置。但有时运维想要临时调整运行参数,于是我尝试了下 CLI (Command-line interface) 。 效果还不错,大家一口气上五楼。

打铁趁热,干脆多尝试下几种 CLI 实现方案。

Library

这种普通的库没啥特点,只按部就班提供诸如添加参数、子命令和参数类型转换之类的 API. 使用这些 API 构造出 parser 去解析命令行输入。

DSL

有点潮,只需完整地写出 Help 文本(DSL 语法就是 Help 内容),其通过解析该文本来生成 parser。非常直观,不依赖任何语言特性,于是所有语言都能实现一致的体验,实际上也都实现了。但和 argparse 一样只是个 parser ,需要额外编码去调用具体的函数。

元编程

这一类的使用体验是最好的,只需实现函数本身,外加一点注解就能自动生成 parser 和函数 binding。 比较依赖语言的特性,至少也得有反射机制才行。比如 Python 连注释(docstring)都能在运行时获得,于是就有 cbox 这样的实现。使用非常简单,我还给它加了子命令支持。

附上 cbox 的子命令实现参考,来自我的 PR。
https://github.com/shmuelamar/cbox/pull/4

import cbox

@cbox.cmd
def hello(name: str):
    """
    greets a person by its name.

    :param name: the name of the person
    """
    print('hello name={name}!'.format(**locals()))

@cbox.cmd
def world(name: str):
    """
    greets a person by its name.

    :param name: the name of the person
    """
    print('world name={name}!'.format(**locals()))

if __name__ == '__main__':
    cbox.main([hello, world])

以上,表现力绝赞!无需多余代码即可自动生成命令行接口。

$ python yourscript.py --help
usage: subcommands.py [-h] {hello,world} ...

which subcommand do you want?

optional arguments:
  -h, --help     show this help message and exit

subcommands:
  {hello,world}
    hello        greets a person by its name.
    world        greets a person by its name.

完。