12 March 2018

给程序添加命令行接口(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.

完。