Docopt/Docopt-ng简单上手
DocOpt
docopt模块的思路和argparse不同,它认为,一个程序的命令行参数和选项应当通过文档自动实现,开发者的任务仅仅只是写出文档而已。
注:DocOpt的思想是通用的,它有多种语言的解析器实现,包括Ruby、Cpp、Golang、Julia和一个用于Shell的二进制解析程序。
安装
现在的Docopt由Jazzband维护,名为docopt-ng,要进行安装,执行:
1  | pip install docopt-ng  | 
传统版本的docopt已经停止维护,但是在大部分发行版仓库中仍然包含:
1  | # Debian  | 
模块内容
文档
“文档”的定义是任意一串遵循BNF范式的Docstring,比如说,你可以自己定义:
1  | docstr = '''  | 
你也可以直接写在__doc__范围内:
1  | '''  | 
Usage:标识了开始解析的部分,文档中至少需要包含这一部分:
1  | '''  | 
程序名实际上可以任取,不过为了方便也可以直接使用sys.argv[0]。
解析
使用docopt.docopt(Str[, argv=['参数']])函数进行解析并返回参数解析字典,argv=表示默认传入的参数值列表。
解析字典中,针对不同类型的选项和参数的保存方式如下:
- 单个参数:以参数名为键,值为字符串。
 - 单个无参选项:以选项名为键,值为布尔值。
 - 单个有参选项:以选项名为键,值为字符串。
 - 不定量参数:以参数名为键,值为字符串列表。
 - 不定量无参选项:以选项名为键,值为整数(选项出现次数)。
 - 不定量有参选项:以选项名为键,值为字符串列表。
 
命令行参数
使用< >包裹起来的部分,或完全是大写字母的部分,会被解析为命令行参数:
1  | '''  | 
1  | '''  | 
然后,就可以通过args字典调用命令行参数,键名就是带有< >的命令行参数名:
1  | args['<pos_arg>']  | 
(docopt-ng)也可以使用点引用方式调用命令行参数,不过此时,键名中的< >则需要省略:
1  | args.pos_arg  | 
不定量参数
有时候,重复的参数数量并不确定,这时候可以使用...来标识:
1  | '''  | 
这样的参数,在字典中对应的值是一个列表。
选项
-或--开头的部分都会被解析为选项,短选项和长选项都支持:
1  | '''  | 
需要注意的是,选项参数使用选项名作为字典的键进行调用,而不是使用选项参数名,字典的键内不会去掉选项名前面的--或-,例如:
1  | args['-o']  | 
(docopt-ng)也可以使用点引用方式调用选项参数,不过此时,键名中的-则需要省略:
1  | args.o  | 
为了方便用户,你还应该把选项(及其长选项)拉出来单独写一段Options:文档:
1  | '''  | 
- 每行选项声明内容的开头都必须是
-,选项部分不能重复。 - 在原则上,短选项是唯一的。短选项之间是不能存在别名关系的。
 - 在选项和选项之间,使用
,或者空格进行分隔都是可以的。 - 选项参数可以使用
< >包裹起来,或是完全使用大写字母。 - 长选项和选项参数之间可以用空格隔开,也可以使用
=连接。 - 在选项和选项描述之间至少要有两个空格。
 - 如果给出了至少一个长选项,那么
docopt会使用最后一个长选项名作为字典键保存选项参数。 - 选项参数可以使用全大写的形式进行标识,也可以使用和之前所说的一致的
<var>形式。 
docopt实际上并不关心选项参数名(因为它不使用选项参数名保存参数值,而是直接使用选项名),因此用户在Options:段中给出的选项参数名并不必须要与Usage:段中一致:
1  | Usage:  | 
但是,选项的类型(是否有参)必须在Usage:段和Options:段中保持一致,也就是说,两个地方必须要么都有选项参数名,要么都没有。
默认值
如果要设置选项的默认值,在Options:段的选项解释内容中添加[default: 值]:
1  | '''  | 
注意,default:设置的默认值总是字符串类型。
在没有设置的情况下:
- 定长参数(选项参数或命令行参数)的默认值是
None。 - 不定长参数(选项参数或命令行参数)的默认值是
[](空列表)。 - 无参选项的默认值是
False。 - 子命令的默认值是
False。 
可选
大部分选项在使用时都应该是可选的,使用[]括起来就可以将选项解析为可选选项:
1  | '''  | 
如果你的所有选项都是可选的,那么你可以干脆地使用[options]替代:
1  | '''  | 
当然,如果只有部分选项不是可选的,也可以把它们单独列出:
1  | '''  | 
[options]会自动排除掉所有在Usage中已经出现的选项。
选项终结符
GNU风格的--选项终结符也可以被解析,只需要在选项和参数之间,或者子命令和参数之间添加[--]即可。这种方式的主要目的是,在某些特殊的情况下传递开头为-的命令行参数:
1  | '''  | 
当然,就算你不写[--],docopt也是接受用户的--的,只是写出会更清晰。
标准输入符
有时候脚本接受来自标准输入的输入,此时只需要将[-]设为参数即可:
1  | '''  | 
子命令
既没有被< >括起来或完全由大写字母组成,也不是以-或--开头的部分,会被解析为子命令:
1  | '''  | 
如果你觉得一行Usage不足以说明所有的子命令,那么也是可以分开写的:
1  | '''  | 
如果在一个子命令的Usage中使用了[options],那么[options]会自动排除掉所有在Usage中已经出现了的选项,例如:
1  | Usage:  | 
多个子命令可以分别编写Options:段:
1  | sub1 Options:  | 
但是,DocOpt并不会对不同的Options:段进行区分,这样做的效果和单独定义一个整体的Options:段没有区别,只是方便用户理解。
分组
有时候我们会希望两个参数或选项要么都出现,要么都不出现,这时候使用()将其结合成组就好了:
1  | '''  | 
互斥
有时候,多个选项或子命令之间的关系是互斥的,只需要有一个就好,对于这种关系,使用(A | B)就可以:
1  | '''  | 
当然,用[A | B]括起来,可以允许任何一个选项都不存在:
1  | '''  | 
这种情况在逻辑上不适用于接受参数的选项。
不定量选项
对于部分选项,我们可能会希望允许其多次出现。这可能是因为我们希望一个无参选项可以重复出现以提高强度,或是希望一个有参选项可以重复出现以多次追加选项参数,这同样可以通过分组和...不定量标识符的方式实现。
例如,对于计数型无参选项:
1  | Usage:  | 
对于允许重复出现的有参选项:
1  | Usage:  | 
你也可以允许它一次都不出现:
1  | Usage:  | 
- 注意,这里使用分组的方式将选项与其选项参数放进一个组内,如果不这么做,那么仅仅意味着选项参数是不定量的。
 
其他
docopt.docopt()函数在解析文档时,默认会自动生成-h、--help和--version(可选)选项的行为。这一行为可以通过一些参数进行修改。
docopt.docopt()函数接受的其他参数:
argv: list[str] | str:解析的命令行部分,默认为None。help: bool或default_help: bool(docopt-ng):是否自动生成-h和--help选项的帮助文档。version: str:版本号,可以用于自动生成在Options:段内定义的--version选项的行为。options_first: bool:只允许选项位于参数之前。即传统命令行选项的行为。