接着前面的《tox 教程》,以及剛翻譯好的《nox文檔》,咱們繼續聊聊 Python 任務自動化的話題。html
nox 的做者在去年的 Pycon US 上,作了一場題爲《Break the Cycle: Three excellent Python tools to automate repetitive tasks》的分享(B站觀看地址:https://b23.tv/av86640235),她介紹了三個任務自動化工具:tox、nox 和 invoke,本文的話題正好就是最後的 invoke。python
invoke 是從著名的遠程部署工具 Fabric 中分離出來的,它與 paramiko 一塊兒是 Fabric 的兩大最核心的基礎組件。正則表達式
除了做爲命令行工具,它專一於「任務執行」(task execution),能夠標註和組織任務,並經過 CLI(command-line interface,即命令行界面) 和 shell 命令來執行任務。shell
一樣是任務自動化工具,invoke 與咱們以前介紹過的 tox/nox 在側重點上有所不一樣:ide
invoke 在 Github 上有 2.7K star,十分受歡迎,接下來咱們看看它如何使用?函數
首先,安裝很簡單:pip install invoke
。工具
其次,簡單使用時有如下要素:post
c
或ctx
或context
。invoke --list
來查看全部任務,運行invoke xxx
來執行名爲 xxx 的任務。命令行中的「invoke」能夠簡寫成「inv」。如下是一個簡單的示例:測試
# 文件名:tasks.py from invoke import task @task def hello(c): print("Hello world!") @task def greet(c, name): c.run(f"echo {name}加油!")
在上述代碼中,咱們定義了兩個任務:ui
以上代碼寫在 tasks.py 文件中,首先導入裝飾器 from invoke import task
,@task 裝飾器能夠不帶參數,也能夠帶參數(參見下一節),被它裝飾了的函數就是一個任務。
上下文參數(即上例的「c」)必需要顯式地指明,若是缺乏這個參數,執行時會拋出異常:「TypeError: Tasks must have an initial Context argument!」
而後在 tasks.py 文件的同級目錄中,打開命令行窗口,執行命令。若是執行的位置找不到這個任務文件,則會報錯:「Can't find any collection named 'tasks'!」
正常狀況下,經過執行inv --list
或者inv -l
,能夠看到全部任務的列表(按字母表順序排序):
>>> inv -l Available tasks: greet hello
咱們依次執行這兩個任務,其中傳參時能夠默認按位置參數傳參,也能夠指定關鍵字傳參。結果是:
>>> inv hello Hello world! >>> inv greet 武漢 武漢加油! >>> inv greet --name="武漢" 武漢加油!
缺乏傳參時,報錯:'greet' did not receive required positional arguments: 'name';多餘傳參時,報錯:No idea what '???' is!
介紹完 invoke 的簡單用法,咱們知道了它所需的幾項要素,也大體知道了它的使用步驟,接下來是它的其它用法。
在上例中,「inv -l」只能看到任務名稱,缺乏必要的輔助信息,爲了增強可讀性,咱們能夠這樣寫:
@task(help={'name': 'A param for test'}) def greet(c, name): """ A test for shell command. Second line. """ c.run(f"echo {name}加油!")
其中,文檔字符串的第一行內容會做爲摘錄,在「inv -l」的查詢結果中展現,並且完整的內容與 @task 的 help 內容,會對應在「inv --help」中展現:
>>> inv -l Available tasks: greet A test for shell command. >>> inv --help greet Usage: inv[oke] [--core-opts] greet [--options] [other tasks here ...] Docstring: A test for shell command. Second line. Options: -n STRING, --name=STRING A param for test
一般一個大任務能夠被分解成一組小任務,反過來,一系列的小任務也可能被串連成一個大任務。在對任務做分解、抽象與組合時,這裏有兩種思路:
第一種思路很容易理解,實現與使用都很簡單,可是其缺點是缺乏靈活性,難於單獨執行其中的某個/些子任務。適用於相對獨立的單個任務,一般也不須要 invoke 就能作到(使用 invoke 的好處是,擁有命令行的支持)。
第二種思路更加靈活,既方便單一任務的執行,也方便多任務的組合執行。實際上,這種場景纔是 invoke 發揮最大價值的場景。
那麼,invoke 如何實現分步任務的組合呢?能夠在 @task 裝飾器的「pre」與「post」參數中指定,分別表示前置任務與後置任務:
@task def clean(c): c.run("echo clean") @task def message(c): c.run("echo message") @task(pre=[clean], post=[message]) def build(c): c.run("echo build")
clean 與 message 任務做爲子任務,能夠單獨調用,也能夠做爲 build 任務的前置與後置任務而組合使用:
>>> inv clean clean >>> inv message message >>> inv build clean build message
這兩個參數是列表類型,便可設置多個任務。另外,在默認狀況下,@task 裝飾器的位置參數會被視爲前置任務,接着上述代碼,咱們寫一個:
@task(clean, message) def test(c): c.run("echo test")
而後執行,會發現兩個參數都被視爲了前置任務:
>>> inv test clean message test
若是要管理不少相對獨立的大型任務,或者須要多個團隊分別維護各自的任務,那麼,就有必要對 tasks.py 做拆分與整合。
例如,如今有多份 tasks.py,彼此是相對完整而獨立的任務模塊,不方便把全部內容都放在一個文件中,那麼,如何有效地把它們整合起來管理呢?
invoke 提供了這方面的支持。首先,只能保留一份名爲「tasks.py」的文件,其次,在該文件中導入其它更名後的任務文件,最後,使用 invoke 的 Collection 類把它們關聯起來。
咱們把本文中第一個示例文件更名爲 task1.py,並新建一個 tasks.py 文件,內容以下:
# 文件名:tasks.py from invoke import Collection, task import task1 @task def deploy(c): c.run("echo deploy") namespace = Collection(task1, deploy)
每一個 py 文件擁有獨立的命名空間,而在此處,咱們用 Collection 能夠建立出一個新的命名空間,從而實現對全部任務的統一管理。效果以下:
>>> inv -l Available tasks: deploy task1.greet task1.hello >>> inv deploy deploy >>> inv task1.hello Hello world! >>> inv task1.greet 武漢 武漢加油!
關於不一樣任務模塊的導入、嵌套、混合、起別名等內容,還有很多細節,請查閱官方文檔瞭解。
某些任務可能須要交互式的輸入,例如要求輸入「y」,按回車鍵後纔會繼續執行。若是在任務執行期間須要人工參與,那自動化任務的能力將大打折扣。
invoke 提供了在程序運行期的監控能力,能夠監聽stdout
和stderr
,並支持在stdin
中輸入必要的信息。
例如,假設某個任務(excitable-program)在執行時會提示「Are you ready? [y/n]」,只有輸入了「y」並按下回車鍵,纔會執行後續的操做。
那麼,在代碼中指定 responses 參數的內容,只要監聽到匹配信息,程序會自動執行相應的操做:
responses = {r"Are you ready? \[y/n\] ": "y\n"} ctx.run("excitable-program", responses=responses)
responses 是字典類型,鍵值對分別爲監聽內容及其迴應內容。需注意,鍵值會被視爲正則表達式,因此像本例中的方括號就要先轉義。
Python 中有很多好用的命令行工具庫,好比標準庫中的argparse
、Flask 做者開源的click
與谷歌開源的fire
等等,而 invoke 也能夠做爲命令行工具庫使用。
(PS:有位 Prodesire 同窗寫了「Python 命令行之旅」的系列文章,詳細介紹了其它幾個命令行工具庫的用法,我在公衆號「Python貓」裏轉載過大部分,感興趣的同窗可查看歷史文章。)
事實上,Fabric 項目最初把 invoke 分離成獨立的庫,就是想讓它承擔解析命令行與執行子命令的任務。因此,除了做爲自動化任務管理工具,invoke 也能夠被用於開發命令行工具。
官方文檔中給出了一個示例,咱們能夠了解到它的基本用法。
假設咱們要開發一個 tester 工具,讓用戶pip install tester
安裝,而此工具提供兩個執行命令:tester unit
和tester intergration
。
這兩個子命令須要在 tasks.py 文件中定義:
# tasks.py from invoke import task @task def unit(c): print("Running unit tests!") @task def integration(c): print("Running integration tests!")
而後在程序入口文件中引入它:
# main.py from invoke import Collection, Program from tester import tasks program = Program(namespace=Collection.from_module(tasks), version='0.1.0')
最後在打包文件中聲明入口函數:
# setup.py setup( name='tester', version='0.1.0', packages=['tester'], install_requires=['invoke'], entry_points={ 'console_scripts': ['tester = tester.main:program.run'] } )
如此打包發行的庫,就是一個功能齊全的命令行工具了:
$ tester --version Tester 0.1.0 $ tester --help Usage: tester [--core-opts] <subcommand> [--subcommand-opts] ... Core options: ... core options here, minus task-related ones ... Subcommands: unit integration $ tester --list No idea what '--list' is! $ tester unit Running unit tests!
上手容易,開箱即用,invoke 不失爲一款能夠考慮的命令行工具庫。更多詳細用法,請查閱文檔 。
invoke 做爲從 Fabric 項目中分離出來的獨立項目,它自身具有一些完整而強大的功能,除了可用於開發命令行工具,它仍是著名的任務自動化工具。
本文介紹了它的基礎用法與 5 個方面的中級內容,相信讀者們會對它產生必定的瞭解。invoke 的官方文檔十分詳盡,限於篇幅,本文再也不詳細展開,若感興趣,請自行查閱文檔哦。
--------------
公衆號:Python貓(ID: python_cat)
頭條號:Python貓
知乎:豌豆花下貓
掘金:豌豆花下貓
公衆號【Python貓】, 本號連載優質的系列文章,有喵星哲學貓系列、Python進階系列、好書推薦系列、技術寫做、優質英文推薦與翻譯等等,歡迎關注哦。