用Click编写Python命令行工具

在编写Python命令行(CLI)应用程序时,使用Click库进行参数解析的深入教程

Python通常被称为胶水语言,因为它非常灵活,并且能够与现有的程序很好地联结在一起。 这意味着很大一部分Python代码被编写为脚本和命令行界面(CLI)。

构建这些命令行界面和工具是非常强大的,因为它使得几乎所有的东西都可以自动化。 因此,随着时间的推移,CLI可能变得相当复杂。

通常从一个非常简单的脚本开始,运行这些python代码来完成一件特定的事情。例如:访问web API并将输出打印到控制台:

仅通过python print_user_agent.py你就可以运行它,它就会打印出 API调用的user-agent。

正如我所说,一个非常简单的脚本。

但是,当这样一个Python命令行脚本变得越来越复杂时,你有什么选择?

这就是我们将在整个教程中看到的内容。您将学习关于Python中构建CLI的基本知识,以及Click如何使其成为更好的体验。

我们将使用这些知识,并从简单的脚本一步一步地通过命令行参数、选项及有用的用法说明CLI。所有这些都使用了一个叫做click的框架。

在本教程的最后,你会知道:

  • 为什么click相比于argparse和optparse来说是一个更好的选择
  • 如何用它创建一个简单的CLI
  • 如何将强制命令行参数添加到您的脚本
  • 如何解析命令行标志和选项
  • 如何通过添加帮助(使用)文本,使命令行应用程序更加便于用户使用。

而且你也会看到如何用最少量的代码来实现所有的功能。

顺便说一下,本教程中的所有代码示例都使用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时用==表示)来获取当前天气:

  • q是我们的地点名称;
  • appid是我们的API密钥。

这使我们可以使用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是一个更好的选择相对argparse和optparse
  • 如何用它创建一个简单的CLI
  • 如何将强制命令行参数添加到您的脚本
  • 如何解析命令行标志和选项;
  • 如何通过添加帮助(使用)文本使您的命令行应用程序更加用户友好

而所有这些都是用最少量的引用! 下面的完整代码示例说明了这一点。你可以自由地使用它来做你自己的实验

如果这启发了你,你应该看看click官方文档以获得更多的功能。 您也可以查看我在2016 PyCon US 关于click的介绍。或者留意我的后续教程,您将在其中学习如何为我们的天气CLI添加更多高级功能。

开心的CLI编码!

原文发布于微信公众号 - IT派(transfer_3255716726)

原文发表时间:2018-06-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏NetCore

一个让人遗忘的角落—Exception(三)

最近有点事,把这个系列给落下了,给大家道个歉,这里还要感谢我的老婆,谢谢她一直对我的支持:) 系列回顾: 1.一个被人遗忘的角落—Exception(一) 2...

2126
来自专栏小灰灰

Quick-Task 动态脚本支持框架之结构设计篇

文章链接:https://liuyueyi.github.io/hexblog/2018/07/23/180723-Quick-Task-动态脚本支持框架之结构...

1043
来自专栏PHP在线

2018最新PHP学习路线整合

PHP是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。

3225
来自专栏Web 开发

最近弄HybridApp的一些心得

SDK的项目折腾了一个月,终于快到收获的时候,把这过程中的一些心得体会记录一下吧~

900
来自专栏腾讯移动品质中心TMQ的专栏

测试人员代码分析5步法

前言 随着测试行业的发展,测试攻城狮也越来越关注产品的源码了。在很多团队中,读懂代码的实现是做好测试的重要一步,但是读懂代码这件事非常依赖代码能力,没有什么捷径...

26010
来自专栏数据魔术师

数据技术|十分钟教会你写网络爬虫程序

写在前面 临近双11,小伙伴们都开始忙着剁手了。蛋是,这个学习还是不能落下的。那么,今天小编又给大家带来什么好玩的玩意儿呢? 那自然是 神奇&&牛掰 爬虫技术 ...

94611
来自专栏微信公众号:Java团长

Java后端程序员1年工作经验总结

  毕业已经一年有余,这一年里特别感谢技术管理人员的器重,以及同事的帮忙,学到了不少东西。这一年里走过一些弯路,也碰到一些难题,也受到过做为一名开发却经常为系统...

2032
来自专栏Golang语言社区

Golang-简洁的并发

转载原文:http://www.yankay.com/go-clear-concurreny/ 多核处理器越来越普及。有没有一种简单的办法,能够让我们写的软件释...

3704
来自专栏木子昭的博客

网络测速插件speedtest

当上网速度很慢的时候, 人们想到的第一件事就进行网络测速,在window上, 只要你安装了360全家桶, 测速功能就是默认安装的, 但其实测速这种功能根本不需要...

3436
来自专栏PHP在线

建立灵巧结构的PHP程序

很早就想写这篇文章了,但一直没有时间完成它。不是说我来告诉大家如何做,我更希望本文只是做为一个引子,与大家来讨论关于如何建立一个有效地、灵活的网络应用程序。 ...

2996

扫码关注云+社区

领取腾讯云代金券