在编写Python命令行(CLI)应用程序时,使用Click库进行参数解析的深入教程
Python通常被称为胶水语言,因为它非常灵活,并且能够与现有的程序很好地联结在一起。 这意味着很大一部分Python代码被编写为脚本和命令行界面(CLI)。
构建这些命令行界面和工具是非常强大的,因为它使得几乎所有的东西都可以自动化。 因此,随着时间的推移,CLI可能变得相当复杂。
通常从一个非常简单的脚本开始,运行这些python代码来完成一件特定的事情。例如:访问web API并将输出打印到控制台:
仅通过python print_user_agent.py你就可以运行它,它就会打印出 API调用的user-agent。
正如我所说,一个非常简单的脚本。
但是,当这样一个Python命令行脚本变得越来越复杂时,你有什么选择?
这就是我们将在整个教程中看到的内容。您将学习关于Python中构建CLI的基本知识,以及Click如何使其成为更好的体验。
我们将使用这些知识,并从简单的脚本一步一步地通过命令行参数、选项及有用的用法说明CLI。所有这些都使用了一个叫做click的框架。
在本教程的最后,你会知道:
而且你也会看到如何用最少量的代码来实现所有的功能。
顺便说一下,本教程中的所有代码示例都使用Python 3.6。它们可能不适用于Python的早期版本,但是如果遇到任何麻烦,请在下面留言,我们将把它整理在一起。
让我们开始吧!
为什么要编写Python命令行脚本和工具?
上面的代码片段仅仅是一个例子,在现实生活中并不是很有用。我在Python开发人员的职业生涯中编写的脚本要复杂得多。它们通常帮助构建,测试和部署应用程序,并使流程可重复。
您可能有自己的经验,并知道这可能是我们日常工作的一大部分:有些脚本仍然保留在你们所构建的项目中,有些对其他团队或项目都有用,甚至可以扩展更多的功能。
在这些情况下,使脚本更加灵活或者可以使用命令行参数进行配置变得非常重要。它使得向脚本提供服务器名称,凭证或任何其他信息成为可能。
这就是像optparse和argparse这样的Python模块,让你的生活变得更轻松。但是,在我们仔细研究这些之前,让我们直接说一下我们的术语。
命令行接口的基础知识
命令行界面(CLI)以可执行文件的名称开头。您可以在控制台中键入它的名称,并访问脚本的主要入口点,例如pip。
根据CLI的复杂性,通常可以将参数传递给脚本,可以是:
1.参数,它是传递给脚本的必需参数。如果您不提供它,CLI将返回一个错误。例如,click是这个命令中的参数:pip install click。
2.或者它可以是一个选项,它是一个可选的(||)参数,结合名称和值部分,如--cache-dir ./my-cache。你告诉CLI应将./my-cache值用作缓存目录。
3.一个特殊选项是启用或禁用特定行为的标志。最常见的可能是 --help。您只需指定名称,CLI将在内部解释该值
使用更复杂的CLI(例如pip或Heroku Toolbelt),您可以访问集合入口的功能。它们通常被称为命令或子命令
当你使用pip install安装Python包时,您可能已经使用了CLI。命令install会告诉CLI您将访问该功能来安装软件包,并使你能访问该特性的参数。
Python 3.x标准库中提供的命令行框架
将命令和参数添加到脚本中是非常强大的,但命令行的解析并不像您想象的那样直截了当。 你应该使用已经解决了这个问题的Python的软件包之一,来替代你自己写。
两个最着名的软件包是optparse和argparse。 它们是遵循“包含电池”原则的Python标准库的一部分。
他们大多提供相同的功能且使用代码非常相似。 最大的不同在于,optparse自Python 3.2以来已被弃用,argparse被认为是在Python中实现CLI的标准
你可以在Python文档中找到更多关于它们的详细信息,来让你知道一个argparse脚本是什么样子的,下面是一个例子:
click vs argparse:一个更好的选择?
你可能正在看上面的代码示例,在想“这些东西是什么意思?”这正是我在使用argparse遇到的一个问题:它不直观,很难阅读。
这就是为什么我爱上了click
click正在解决与optparse和argparse相同的问题,但使用方法稍微不同。它使用装饰器的概念。这需要命令是可以使用装饰器包装的函数。
丹写了一个很好的介绍,如果这是你第一次听到这个词,或许你想快速学习。
作者Armin Ronacher详细描述了他为什么写这个框架。您可以阅读文档中的“Why Click?”部分,我鼓励您看一下。
我使用click的主要原因是你可以使用少量代码轻松构建功能丰富的CLI。即使您的CLI增长并且变得更加复杂,代码也很容易阅读。
通过Click构建一个简单的Python命令行界面
我已经谈了很多CLI和框架。我们来看看用click来构建一个简单的CLI是什么意思。与本教程中的第一个示例类似,我们可以创建一个简单的基于click的CLI,它向控制台打印一些东西。这并不是很费力:
首先,我们现在不用担心最后两行,当文件作为脚本执行时,这只是Python(稍微不直观)的方式来运行主函数。
正如你所看到的,我们所要做的就是创建一个函数并添加@ click.command()装饰器。 这将它变成一个click命令,这是我们的脚本的主要入口点。 你现在可以在命令行上运行它,你会看到类似这样的东西:
click 之所以比较美观是因为,我们免费获得一些额外的功能。 我们没有实现任何帮助功能,但添加了--help选项,您将看到一个打印到命令行的基本帮助页面:
Click更实际的PythonCLI示例
现在你已经知道click是如何使得建立一个简单的CLI更容易了,我们将看一个稍微更现实的例子。我们将构建一个允许我们与Web API 进行交互的程序。最近每个人都会使用,它们让我们访问一些更酷的数据。
本教程其余部分将介绍的API是OpenWeatherMap API。 它提供当前天气以及特定位置的五天预报。 我们将从他们的API示例返回当前天气的位置。
在开始编写代码之前,我喜欢尝试使用API来更好地理解它是如何工作的。 我想你应该知道的一个工具是HTTPie,我们可以使用它来调用示例API并查看返回的结果。 你甚至可以尝试他们的在线终端来运行它,无需安装。
让我们来看看当我们将API中的位置设置为london时会发生什么:
如果你正在用这样的面孔查看屏幕?因为上面的例子包含一个API密钥,所以不要担心这是他们提供的示例API密钥。
上面例子中比较重要的一点是,我们发送两个查询参数(使用HTTPie时用==表示)来获取当前天气:
这使我们可以使用Python和Requests库创建一个简单的实现(为简单起见,我们将忽略错误处理和失败请求)。
这个函数使用两个查询参数向天气API发出一个简单的请求。 它需要一个强制的参数location,它被假定为一个字符串。 我们还可以通过在函数调用中传递api_key来提供API密钥。 它是可选的,可以使用示例键作为默认值。
这里是我们目前伦敦的天气,形成Python REPL:
click 解析一个必选参数
简单的current_weather函数允许我们使用用户提供的自定义位置来构建我们的CLI。 我希望它能像这样工作:
你可能已经猜到了,这次调用的位置就是我之前介绍的一个参数。 这是因为它是我们天气CLI的强制性参数。
我们如何在Click中实现? 这很简单,我们使用一个名为参数的装饰器。 谁会想到?
我们先来看一个简单的例子,通过定义参数的位置来修改它。
你可以看到,我们所要做的就是添加一个额外的装饰器到我们的主要功能,并给它一个名字。Click使用该名称作为变量传递到包装函数的参数中。
在我们的例子中,命令行参数location的值将作为位置参数传递给主函数。有道理吧?
你也可以在你的名字中使用破折号( - ),例如api-key,在这个函数中,Click会将名字的中划线变为下划线。例如main(api_key)。
main的实现只需使用我们的current_weather函数来获取CLI调用者提供的位置的天气。 然后我们使用一个简单的打印语句输出天气信息||
完成!
如果这个打印语句对你来说看起来很奇怪,那是因为这是一种用Python 3.6+格式化字符串的新方法,称为f-string格式。 你应该查看 “Python中进行字符串格式化的4种主要方法”来了解更多。
cllick 解析可选参数
你可能已经找到了我们上面使用的示例API的一个小小的缺陷,你是一个聪明人
是的,这是一个静态的端点,从2017年1月起总是返回伦敦的天气。所以让我们用一个真实的API密钥来请求实际的API。
我们需要改变的第一件事是当前天气的URL端点。 我们可以通过在OpenWeatherMap文档中将current_weather函数中的url替换为端点来实现:
我们刚刚做出的更改将会破坏我们的CLI,因为默认API密钥对真实API无效。 该API将返回一个401 UNAUTHORIZED HTTP状态码。 不相信我? 这是证明:
所以让我们添加一个新的参数给我们的CLI,允许我们指定API密钥。 但首先,我们必须决定这应该是一个参数还是一个选项。
我们使它成为一个option,因为添加一个像--api-key这样的命名参数使得它更加明确和自描述。
以下是我认为用户应该运行它的方式:
很好很容易。 所以让我们看看我们如何将它添加到我们现有的click命令。
再来一次,我们正在为我们的main函数添加一个装饰器。 这一次,我们使用非常直观的命名@ click.option,并添加了我们的option名称,包括前双破折号( -- )。 正如你所看到的,我们也可以用一个短划线( - )来提供一个快捷方式来保存用户的一些输入。
我之前提到,click从较长的版本创建传递给主函数的参数。 在option的情况下,它将划破前面的破折号并将其变成snake_case的情况。 --api-key变成api_key。
我们必须做的最后一件事是将API密钥传递给我们的current_weather函数。
我们使CLI用户可以使用自己的密钥并查看任何位置:
看着我的窗口,我可以证实这是真的。
将自动生成的使用说明添加到您的Python命令行工具中
你可以安慰自己,你已经用最少量的Boilerplate_code构建了一个很棒的小CLI。但是在你休息或者享受一杯饮料之前, 通过添加一些文档,让我们确保一个新的用户能够知道如何运行我们的小CLI ...(不要跑,超级简单的。)
首先让我们来看看在我们做了所有更改之后,--help标志将会显示什么。 正如你所看到的,所有努力都不是白费力的:
我们想要解决的第一件事是我们的API密钥选项丢失的描述。 我们所要做的就是向@click.option装饰器提供一个帮助文本:
我们要做的第二个也是最后一个更改是添加整个click命令的文档。 而最简单的方式就是添加一个文档字符串到我们的main函数。 是的,我们应该这样做,所以这不是额外的工作:
综合起来,我们的天气工具得到了非常好的输出。
我希望在这一点上,你感觉到了当我第一次发现click时的感觉:
带有click的Python CLI:摘要&回顾
好的,我们已经在本教程中介绍了大量的内容。 现在是您为自己感到自豪的时候了。 以下是你所学到的:
而所有这些都是用最少量的引用! 下面的完整代码示例说明了这一点。你可以自由地使用它来做你自己的实验
如果这启发了你,你应该看看click官方文档以获得更多的功能。 您也可以查看我在2016 PyCon US 关于click的介绍。或者留意我的后续教程,您将在其中学习如何为我们的天气CLI添加更多高级功能。
开心的CLI编码!