帶你了解3個(gè)Python命令行工具
用 Click、Docopt 和 Fire 庫(kù)寫(xiě)你自己的命令行應(yīng)用。
有時(shí)對(duì)于某項(xiàng)工作來(lái)說(shuō)一個(gè)命令行工具就足以勝任。命令行工具是一種從你的 shell 或者終端之類(lèi)的地方交互或運(yùn)行的程序。Git 和 Curl 就是兩個(gè)你也許已經(jīng)很熟悉的命令行工具。
當(dāng)你有一小段代碼需要在一行中執(zhí)行多次或者經(jīng)常性地被執(zhí)行,命令行工具就會(huì)很有用。Django 開(kāi)發(fā)者執(zhí)行 ./manage.py runserver
命令來(lái)啟動(dòng)他們的網(wǎng)絡(luò)服務(wù)器;Docker 開(kāi)發(fā)者執(zhí)行 docker-compose up
來(lái)啟動(dòng)他們的容器。你想要寫(xiě)一個(gè)命令行工具的原因可能和你一開(kāi)始想寫(xiě)代碼的原因有很大不同。
對(duì)于這個(gè)月的 Python 專(zhuān)欄,我們有 3 個(gè)庫(kù)想介紹給希望為自己編寫(xiě)命令行工具的 Python 使用者。
Click
Click 是我們***的用來(lái)開(kāi)發(fā)命令行工具的 Python 包。其:
- 有一個(gè)富含例子的出色文檔
- 包含說(shuō)明如何將命令行工具打包成一個(gè)更加易于執(zhí)行的 Python 應(yīng)用程序
- 自動(dòng)生成實(shí)用的幫助文本
- 使你能夠疊加使用可選和必要參數(shù),甚至是 多個(gè)命令
- 有一個(gè) Django 版本(
django-click
)用來(lái)編寫(xiě)管理命令
Click 使用 @click.command()
去聲明一個(gè)函數(shù)作為命令,同時(shí)可以指定必要和可選參數(shù)。
# hello.py
import click
@click.command()
@click.option('--name', default='', help='Your name')
def say_hello(name):
click.echo("Hello {}!".format(name))
if __name__ == '__main__':
say_hello()
@click.option()
修飾器聲明了一個(gè) 可選參數(shù) ,而 @click.argument()
修飾器聲明了一個(gè) 必要參數(shù)。你可以通過(guò)疊加修飾器來(lái)組合可選和必要參數(shù)。echo()
方法將結(jié)果打印到控制臺(tái)。
$ python hello.py --name='Lacey'
Hello Lacey!
Docopt
Docopt 是一個(gè)命令行工具的解析器,類(lèi)似于命令行工具的 Markdown。如果你喜歡流暢地編寫(xiě)應(yīng)用文檔,在本文推薦的庫(kù)中 Docopt 有著***的格式化幫助文本。它不是我們***的命令行工具開(kāi)發(fā)包的原因是它的文檔猶如把人扔進(jìn)深淵,使你開(kāi)始使用時(shí)會(huì)有一些小困難。然而,它仍是一個(gè)輕量級(jí)的、廣受歡迎的庫(kù),特別是當(dāng)一個(gè)漂亮的說(shuō)明文檔對(duì)你來(lái)說(shuō)很重要的時(shí)候。
Docopt 對(duì)于如何格式化文章開(kāi)頭的 docstring 是很特別的。在工具名稱(chēng)后面的 docsring 中,頂部元素必須是 Usage:
并且需要列出你希望命令被調(diào)用的方式(比如:自身調(diào)用,使用參數(shù)等等)。Usage:
需要包含 help
和 version
參數(shù)。
docstring 中的第二個(gè)元素是 Options:
,對(duì)于在 Usages:
中提及的可選項(xiàng)和參數(shù),它應(yīng)當(dāng)提供更多的信息。你的 docstring 的內(nèi)容變成了你幫助文本的內(nèi)容。
"""HELLO CLI
Usage:
hello.py
hello.py <name>
hello.py -h|--help
hello.py -v|--version
Options:
<name> Optional name argument.
-h --help Show this screen.
-v --version Show version.
"""
from docopt import docopt
def say_hello(name):
return("Hello {}!".format(name))
if __name__ == '__main__':
arguments = docopt(__doc__, version='DEMO 1.0')
if arguments['<name>']:
print(say_hello(arguments['<name>']))
else:
print(arguments)
在最基本的層面,Docopt 被設(shè)計(jì)用來(lái)返回你的參數(shù)鍵值對(duì)。如果我不指定上述的 name
調(diào)用上面的命令,我會(huì)得到一個(gè)字典的返回值:
$ python hello.py
{'--help': False,
'--version': False,
'<name>': None}
這里可看到我沒(méi)有輸入 help
和 version
標(biāo)記并且 name
參數(shù)是 None
。
但是如果我?guī)е粋€(gè) name
參數(shù)調(diào)用,say_hello
函數(shù)就會(huì)執(zhí)行了。
$ python hello.py Jeff
Hello Jeff!
Docopt 允許同時(shí)指定必要和可選參數(shù),且各自有著不同的語(yǔ)法約定。必要參數(shù)需要在 ALLCAPS
和 <carets>
中展示,而可選參數(shù)需要單雙橫杠顯示,就像 --like
。更多內(nèi)容可以閱讀 Docopt 有關(guān) patterns 的文檔。
Fire
Fire 是谷歌的一個(gè)命令行工具開(kāi)發(fā)庫(kù)。尤其令人喜歡的是當(dāng)你的命令需要更多復(fù)雜參數(shù)或者處理 Python 對(duì)象時(shí),它會(huì)聰明地嘗試解析你的參數(shù)類(lèi)型。
Fire 的 文檔 包括了海量的樣例,但是我希望這些文檔能被更好地組織。Fire 能夠處理 同一個(gè)文件中的多條命令、使用 對(duì)象 的方法作為命令和 分組 命令。
它的弱點(diǎn)在于輸出到控制臺(tái)的文檔。命令行中的 docstring 不會(huì)出現(xiàn)在幫助文本中,并且?guī)椭谋疽膊灰欢?biāo)識(shí)出參數(shù)。
import fire
def say_hello(name=''):
return 'Hello {}!'.format(name)
if __name__ == '__main__':
fire.Fire()
參數(shù)是必要還是可選取決于你是否在函數(shù)或者方法定義中為其指定了一個(gè)默認(rèn)值。要調(diào)用命令,你必須指定文件名和函數(shù)名,比較類(lèi)似 Click 的語(yǔ)法:
$ python hello.py say_hello Rikki
Hello Rikki!
你還可以像標(biāo)記一樣傳參,比如 --name=Rikki
。
額外贈(zèng)送:打包!
Click 包含了使用 setuptools
打包 命令行工具的使用說(shuō)明(強(qiáng)烈推薦按照說(shuō)明操作)。
要打包我們***個(gè)例子中的命令行工具,將以下內(nèi)容加到你的 setup.py
文件里:
from setuptools import setup
setup(
name='hello',
version='0.1',
py_modules=['hello'],
install_requires=[
'Click',
],
entry_points='''
[console_scripts]
hello=hello:say_hello
''',
)
任何你看見(jiàn) hello
的地方,使用你自己的模塊名稱(chēng)替換掉,但是要記得忽略 .py
后綴名。將 say_hello
替換成你的函數(shù)名稱(chēng)。
然后,執(zhí)行 pip install --editable
來(lái)使你的命令在命令行中可用。
現(xiàn)在你可以調(diào)用你的命令,就像這樣:
$ hello --name='Jeff'
Hello Jeff!
通過(guò)打包你的命令,你可以省掉在控制臺(tái)鍵入 python hello.py --name='Jeff'
這種額外的步驟以減少鍵盤(pán)敲擊。這些指令也很可能可在我們提到的其他庫(kù)中使用。