本文翻譯自 Python Plumbum 開源庫的官方文檔 Plumbum CLI 部分,主要介紹如何使用 Plumbum CLI 工具包來開發 Python 命令行應用程序,這是一個非常 Pythonic、容易使用、功能強大的工具包,非常值得廣大 Python 程序員掌握並使用。
輕鬆執行程序的另一方面是輕鬆編寫 CLI 程序。Python 腳本一般使用 optparse 或者最新的 argparse 及其衍生品來開發命令行工具,但是所有這些表現力有限,而且非常不直觀(甚至不夠 Pythonic)。Plumbum 的 CLI 工具包提供了一個程序化的方法來構建命令行應用程序,不需要創建一個解析器對象,然後填充一系列“選項”,該 CLI 工具包使用內省機制將這些原語轉義成 Pythonic 結構。
總體來看,Plumbum CLI 應用程序是一個繼承自 plumbum.cli.Application 的類。這些類定義了一個 main 方法,並且可選地公開出方法和屬性來作為命令行的選項。這些選項可能需要參數,而任何剩餘的位置參數會根據 main 函數的聲明來將其賦予 main 方法。一個簡單的 CLI 應用程序看起來像如下這樣:
你可以運行該程序:
到現在為止,你只看到了非常基本的使用。我們現在開始探索該庫。
新版本 1.6.1:你可以直接運行應用程序MyApp,不需要參數,也不需要調用 .main。
應用程序
Application類是你的應用程序的“容器”,該“容器”由一個你需要實現的main方法和任何數量公開選項函數和屬性。你的應用程序的入口是類方法 run,該方法實例化你的類、解析參數、調用所有的選項函數,然後使用給的位置參數來調用main函數。為了從命令行運行你的應用程序,你所要做的是:
除了 run 和 main,Application 類還公開了兩個內置的選項函數:help 和 version,分別用於顯示幫助和程序的版本。默認情況下,--hep 和 -h 會調用 help,--version 和 -v 會調用 version,這些函數被調用後會顯示相應的信息然後退出(沒有處理任何其他選項)。
你可以通過定義類屬性來自定義 help 和 version 顯示的信息,比如 PROGNAME、 VERSION 和 DESCRIPTION。舉例:
顏色
新版本 1.6
該庫也支持終端字符顏色控制。你可以直接將 PROGNAME, VERSION 和 DESCRIPTION 變為帶顏色的字符串。如果你給 PROGNAME 設置了顏色,你會得到自定義的程序名字和顏色。使用方法字符串的顏色可以通過設置 COLOR_USAGE 來生效,不同選項組的顏色可以通過設置 COLOR_GROUPS 字典來生效。
舉例如下:
選項函數
switch裝飾器是該 CLI 開發工具包的“靈魂”,它會公開你的 CLI 應用程序的方法來作為 CLI 命令行選項,這些方法運行通過命令行來調用。我們測試下如下應用:
當程序運行時,選項函數通過相應的參數被調用。比如,
$ ./myapp.py --log-to-file=/tmp/log 將被轉化成調用 app.log_to_file("/tmp/log")。在選項函數被執行後,程序的控制權會被傳遞到 main 方法。
注意
方法的文檔字符串和參數名字會被用來渲染幫助信息,儘量保持你的代碼DRY。
autoswitch 可以從函數名字中推斷出選項的名稱,舉例如下:
這會將選項函數和 --log-to-file 綁定。
選項參數
如上面例子所示,選項函數可能沒有參數(不包括 self)或者有一個參數。如果選項函數接受一個參數,必須指明該參數的類型。如果你不需要特殊的驗證,只需傳遞str,否則,您可能會傳遞任何類型(或實際上可調用的任何類型),該類型將接收一個字符串並將其轉換為有意義的對象。如果轉換是不可行的,那麼會拋出 TypeError 或者 ValueError 異常。
舉例:
工具包包含兩個額外的“類型”(或者是是驗證器):Range 和 Set。Range 指定一個最小值和最大值,限定一個整數在該範圍內(閉區間)。Set 指定一組允許的值,並且期望參數匹配這些值中的一個。示例如下:
注意工具包中還有其他有用的驗證器:ExistingFile(確保給定的參數是一個存在的文件),ExistingDirectory(確保給定的參數是一個存在的目錄),NonexistentPath(確保給定的參數是一個不存在的路徑)。所有這些將參數轉換為本地路徑。
可重複的選項
很多時候,你需要在同一個命令行中多次指定某個選項。比如,在 gcc 中,你可能使用 -I參數來引入多個目錄。默認情況下,選項只能指定一次,除非你給 switch 裝飾器傳遞 list = True 參數。
注意選項函數只被調用一次,它的參數將會變成一個列表。
強制的選項
如果某個選項是必須的,你可以給 switch 裝飾器傳遞 mandatory = True 來實現。這樣的話,如果用戶不指定該選項,那麼程序是無法運行的。
選項依賴
很多時候,一個選項的出現依賴另一個選項,比如,如果不給定 -y 選項,那麼 -x 選項是無法給定的。這種限制可以通過給 switch 裝飾器傳遞 requires 參數來實現,該參數是一個當前選項所依賴的選項名稱列表。如果不指定某個選項所依賴的其他選項,那麼用戶是無法運行程序的。
警告選項函數的調用順序和命令行指定的選項的順序是一致的。目前不支持在程序運行時計算選項函數調用的拓撲順序,但是將來會改進。
選項互斥
有些選項依賴其他選項,但是有些選項是和其他選項互斥的。比如,--verbose 和 --terse同時存在是不合理的。為此,你可以給 switch 裝飾器指定 excludes 列表來實現。
選項分組
如果你希望在幫助信息中將某些選項組合在一起,你可以給 switch 裝飾器指定 group = "Group Name", Group Name 可以是任意字符串。當顯示幫助信息的時候,所有屬於同一個組的選項會被聚合在一起。注意,分組不影響選項的處理,但是可以增強幫助信息的可讀性。
選項屬性
很多時候只需要將選項的參數存儲到類的屬性中,或者當某個屬性給定後設置一個標誌。為此,工具包提供了 SwitchAttr,這是一個數據描述符,用來存儲參數。 該工具包還提供了兩個額外的 SwitchAttr:Flag(如果選項給定後,會給其賦予默認值)和 CountOf (某個選項出現的次數)。
環境變量
新版本 1.6
你可以使用envname參數將環境變量作為SwitchAttr的輸入。舉例如下:
在命令行給定變量值會覆蓋相同環境變量的值。
Main
一旦當所有命令行參數被處理後 ,main方法會獲取程序的控制,並且可以有任意數量的位置參數,比如,在cp -r /foo /bar中,/foo和/bar是位置參數。程序接受位置參數的數量依賴於main函數的聲明:如果main方法有 5 個參數,2 個是有默認值的,那麼用戶最少需要提供 3 個位置參數並且總數量不能多於 5 個。如果main方法的聲明中使用的是可變參數(*args),那麼位置參數的個數是沒有限制的。
注意 該方法的聲明也用於生成幫助信息,例如:
Usage: [SWITCHES] src dst [mode='normal']
使用可變參數:
位置驗證
你可以使用cli.positional裝飾器提供的驗證器來驗證位置參數。只需在裝飾器中傳遞與main函數中的相匹配的驗證器即可。例如:
如果你的程序只在 Python 3 中運行,你可以使用註解來指定驗證器,例如:
子命令
新版本 1.1
隨著 CLI 應用程序的擴展,功能變的越來越多,一個通常的做法是將其邏輯分成多個子應用(或者子命令)。一個典型的例子是版本控制系統,比如git,git 是根命令,在這之下的子命令比如 commit 或者 push 是嵌套的。git 甚至支持命令別名,這運行用戶自己創建一些子命令。Plumbum 寫類似這樣的程序是很輕鬆的。
在我們開始瞭解代碼之前,先強調兩件事情:
在 Plumbum 中,每個子命令都是一個完整的 cli.Application 應用,你可以單獨執行它,或者從所謂的根命令中分離出來。當應用程序單獨執行是,它的父屬性是 None,當以子命令運行時,它的父屬性指向父應用程序。同樣,當父應用使用子命令執行時,它的內嵌命令被設置成內嵌應用。
每個子命令只負責它自己的選項參數(直到下一個子命令)。這允許應用在內嵌應用調用之前來處理它自己的選項和位置參數。例如 git --foo=bar spam push origin --tags:根應用 git 負責選項 --foo 和位置選項 spam ,內嵌應用 push 負責在它之後的參數。從理論上講,你可以將多個子應用程序嵌套到另一個應用程序中,但在實踐中,通常嵌套層級只有一層。
這是一個模仿版本控制系統的例子 geet。我們有一個根應用 Geet ,它有兩個子命令 GeetCommit 和 GeetPush:這兩個子命令通過 subcommand 裝飾器來將其附加到根應用。
注意
由於 GeetCommit 也是一個 cli.Application,因此你可以直接調用 GeetCommit.run (這在應用的上下文是合理的)
你也可以不用裝飾器而使用 subcommand 方法來附加子命令:Geet.subcommand("push", GeetPush)
以下是運行該應用程序的示例:
配置解析器
應用程序的另一個常見的功能是配置文件解析器,解析後臺 INI 配置文件:Config(或者ConfigINI)。使用示例:
如果配置文件不存在,那麼將會以當前的
key 和默認的 value 來創建一個配置文件,在調用 .get 方法時會得到默認值,當上下文管理器存在時,文件會被創建。如果配置文件存在,那麼該文件將會被讀取並且沒有任何改變。你也可以使用 語法來強制設置一個值或者當變量不存在時獲取到一個 ValueError。如果你想避免上下文管理器,你也可以使用 .read 和 .write。
ini 解析器默認使用 [DEFAULT] 段,就像 Python 的 ConfigParser。如果你想使用一個不同的段,只需要在 key 中通過 . 將段和標題分隔開。比如 conf['section.item'] 會將 item放置在 [section] 下。所有存儲在 ConfigINI 中的條目會被轉化成 str,str 是經常返回的。
終端實用程序
在 plumbum.cli.terminal 中有多個終端實用程序,用來幫助製作終端應用程序。
get_terminal_size(default=(80,25)) 允許跨平臺訪問終端屏幕大小,返回值是一個元組 (width, height)。還有幾個方法可以用來詢問用戶輸入,比如 readline, ask, choose和 prompt 都是可用的。
Progress(iterator) 可以使你快速地從迭代器來創建一個進度條。簡單地打包一個 slow 迭代器並迭代就會生成一個不錯的基於用戶屏幕寬度的文本進度條,同時會顯示剩餘時間。如果你想給 fast 迭代器創建一個進度條,並且在循環中包含代碼,那麼請使用 Progress.wrap 或者 Progress.range。例如:
如果在終端中有其他輸出,但是仍然需要一個進度條,請傳遞 has_output=True 參數來禁止進度條清除掉歷史輸出。
在 plumbum.cli.image 中提供了一個命令行繪圖器(Image)。它可以繪製一個類似 PIL 的圖像:
Image.show_pil(im)
Image 構造函數接受一個可選的 size 參數(如果是 None,那麼默認是當前終端大小)和一個字符比例,該比例來自當前字符的高度和寬度的度量,默認值是 2.45。如果設置為 None,ratio 將會被忽略,圖像不再被限制成比例縮放。要直接繪製一個圖像,show 需要一個文件名和一對參數。show_pil 和 show_pil_double 方法直接接受一個 PIL-like 對象。為了從命令行繪製圖像,該模塊可以直接被運行:python -m plumbum.cli.image myimage.png。
需要python教程+PDF電子書的小夥伴
請關注、轉發、私信我"學習"就能免費獲取教程+電子書。
閱讀更多 Java程序媛 的文章