比较Python命令行解析库 — Argparse、Docopt与Click(1)

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

大约一年前,我开始了一份工作,在该工作中建立命令行应用程序很常见。当时,我使用argparse相当多,并想探索其它可用的库。我发现最受欢迎的替代品是clickdocopt。在我的探索过程中,我还发现每个库除了“为什么使用”,没有多少内容可供三个库做完整的比较。于是有了本文!

0.png

本文是Kyle Purdon发布在客座博客上的,他是位于Denver的Bitly的一名应用工程师。

如果你想,可以直接看源代码,尽管没有本文的比较和逐步构造直接看源码真的没有多大好处。

本文采用库的版本如下:

1.png

命令行示例

我们正在创建的命令行应用程序将具有以下接口:

python [file].py [command] [options] NAME

基本用法

2.png

w/选项用法(标志)

3.png

本文将比较每个库实现以下功能的方法:

  1. 命令(hello,goodbye)

  2. 参数(name)

  3. 选项/标志 (—greeting=<str>, —caps)

附加功能:

  1. 版本信息输出 (-v/--version)

  2. 自动化的帮助信息

  3. 错误处理

正如你所期望的,argparse,docopt,和click实现了所有这些功能(正如任何完整的命令行库)。这意味着这些功能的实际实现是我们将要比较的。每个库采取的方法不同,这给比较增添了几分乐趣 - argparse=standard, docopt=docstrings, click=decorators。

额外部分

  1. 我一直非常想了解任务运行库如fabric,并且它在python 3中替换invoke来创建简单的命令行接口,因此我将尝试把相同的接口与invoke一起使用。

  2. 打包命令行应用程序时需要一些额外的步骤,因此我也将介绍这些步骤!

命令

让我们从设置每个库的基本框架(没有参数或选项)开始。

Argparse

4.png

现在我们具有两个命令(hellogoodbye)和内置帮助信息。注意,命令hello带选项运行时,帮助信息将更改。

5.png

Docopt

6.png

现在我们再次具有两个命令(hellogoodbye)和内置帮助信息。注意,命令hello带选项运行时,帮助信息不会更改。另外,Options部分需要显示指定commands.py -h | --help来获取帮助命令。然而,如果我们没有显示指定,它们将不会作为选项出现在输出帮助信息中。

7.png

Click

8.png

现在我们具有两个命令(hello, goodbye)和内置帮助信息。注意,命令hello带选项运行时,帮助信息将更改。

9.png

你可以看到,我们有不同的方法来构建基本的命令行应用程序。接下来,我们添加NAME参数,以及输出每个工具结果的逻辑。

参数

在本节中,我们将向上一节显示的代码中添加新的逻辑。我们将给新的行添加注释,说明其目的。命令行应用程序要求输入参数(又名位置参数)。在本例中,我们添加一个必需的name参数,以便该工具可以问候特定的人。

Argparse

为了添加一个参数传递给子命令,我们使用add_argument方法。当调用命令时,为了执行正确的逻辑,我们使用set_defaults方法设置默认函数。最后,在运行时解析参数后,我们通过调用args.func(args)执行默认函数。

10.png

11.png

Docopt

为了增加一个选项,我们增加<name>到docstring。<>用于指定位置参数。为了执行正确的逻辑,我们必须检查在运行时if arguments['hello']:命令是True,然后调用正确的函数。

12.png

13.png

注意,帮助信息不是特定于子命令,而是整个程序的docstring。

Click

为了给click命令增加一个参数,使用@click.argument装饰器。此例中,我们仅传递参数名,但还有更多的选项,其中一些我们以后会使用。由于我们使用参数来装饰逻辑(函数),所以不需要做任何事情来设置或调用正确的逻辑。

14.png

15.png

标志/选项

在本节中,我们将再次向上一节显示的代码中添加新的逻辑。我们将给新的行添加注释,说明其目的。选项是非必需的输入,可以用来更改命令行应用程序的执行。标志是一个布尔(True/False)子集的选项。例如:--foo=bar将传递bar作为foo选项的值,并且如果给出选项,--baz(如果定义为标志)将传递值True,否则传递值False。

本例中,我们将添加--greeting=[greeting]选项和--caps标志。Hello和Goodbye的greeting选项有默认值,并且允许用户传递自定义问候。例如,--greeting=Wazzup工具将响应为Wazzup, [name]!。使用--caps标志将大写整个响应。例如,使用--caps,工具将响应为HELLO, [NAME]!。

Argparse

16.png

17.png

Docopt

一旦我们遇到添加默认选项的情况,就遇到了在docopt中实现基本命令的障碍。让我们继续来说明这个问题。

18.png

现在,运行以下命令,看看发生了什么:

19.png

什么?!因为我们仅可以为Hello和Goodbye的--greeting选项设置一个默认,现在回应是Hello, Kyle!。为了让这工作,我们需要按照docopt提供的git示例。重构代码如下所示:

20-1.png

20-2.png

如你所见,hello|goodbye子命令现在有自己的docstrings绑定到变量HELLO和GOODBYE。当工具执行后,它使用新的参数command,来决定解析什么。这不仅纠正了我们只有一个默认的问题,而且现在也具有子命令特定的帮助信息。

21.png

另外,我们所有新的选项/标志也工作:

22.png

Click

为了增加greeting和caps选项,我们使用装饰器@click.option。我们有默认的问候,现在我们将逻辑放在一个单独的函数(def greeter(**kwargs):)中。

23-1.png

23-2.png

24.png

版本选项(—version)

在本节中,我们将展示如何添加一个--version参数到每个工具。为了简单起见,我们将硬编码版本为1.0.0。记住,在生产应用中,你将想从安装的应用程序中得到这。以下简单的处理可以实现这一点:

25.png

确定版本的第二个选项是,在发布新版本时,自动版本软件更改文件中定义的版本号。bumpversion使这成为可能。但是不推荐此方法,因为它很容易失去同步。通常,最好的做法是在尽可能少的地方保留版本号。

由于添加硬编码版本选项的实现相当简单,我们将使用...来表示跳过上一节的代码。

Argparse

对于argparse,我们再次需要使用add_argument方法,这次传入参数action='version'和version的值。我们将此方法应用到根解析器(而不是hello或goodbye子解析器)。

26.png

27.png

Docopt

为了给docopt增加--version,我们把它作为一个选项,添加到基本的docstring。此外,我们添加version参数到docopt的第一调用(解析基本的docstring)。

28.png

29.png

Click

Click提供一个方便的装饰器@click.version_option。为了添加它,我们装饰greet函数(主要@click.group功能)。

30.png

31.png

改进帮助文档(-h/—help)

完成我们的应用程序的最后一步是为每个工具改进帮助文档。我们将想要确保通过-h和--help都可以访问帮助信息,并且每个参数和选项有一定程度的描述。

Argparse

默认情况下,argparse提供了-h和--help,因此我们不需要添加什么。然而我们目前的帮助文档对于子命令缺乏信息,如--caps和--greeting做什么和name参数是什么。

32.png

为了添加更多信息,我们使用add_argument方法的help参数。

33.png

现在,当我们传入help标志,得到一个更完整的结果:

34.png

Docopt

本节是docopt的亮点。因为我们编写文档作为命令行接口本身的定义,所以我们已经完成了帮助文档。此外,也已经提供-h和--help。

35.png

Click

添加帮助文档到click与argparse很相似。需要添加help参数到所有的@click.option装饰器。

36.png

然而,默认情况下click不提供-h。需要使用context_settings参数来重写默认help_option_names。

37.png

现在,click帮助文档完成了。

38.png

英文原文:https://realpython.com/blog/python/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
译者:小雨