Python 命令列之旅:深入 click 之增強功能
作者:HelloGitHub-Prodesire
HelloGitHub 的《講解開源專案》系列,專案地址:https://github.com/HelloGitHub-Team/Article
一、前言
在前面三篇文章中,我們介紹了 click
中的引數、選項和命令,本文將介紹 click
錦上添花的功能,以幫助我們更加輕鬆地打造一個更加強大的命令列程式。
本系列文章預設使用 Python 3 作為直譯器進行講解。
若你仍在使用 Python 2,請注意兩者之間語法和庫的使用差異哦~
二、增強功能
2.1 Bash 補全
Bash 補全是 click
提供的一個非常便捷和強大的功能,這是它比 argpase
docopt
強大的一個表現。
在命令列程式正確安裝後,Bash 補全才可以使用。而如何安裝可以參考 setup 整合。Click 目前僅支援 Bash 和 Zsh 的補全。
2.1.1 補全能力
通常來說,Bash 補全支援對子命令、選項、以及選項或引數值得補全。比如:
$ repo <TAB><TAB>
clone commit copy delete setuser
$ repo clone -<TAB><TAB>
--deep --help --rev --shallow -r
此外,click
autocompletion
引數。autocompletion
需要指定為一個回撥函式,並且返回字串的列表。此函式接受三個引數:
ctx
—— 當前的 click 上下文args
傳入的引數列表incomplete
正在補全的詞
這裡有一個根據環境變數動態生成補全的示例:
import os def get_env_vars(ctx, args, incomplete): return [k for k in os.environ.keys() if incomplete in k] @click.command() @click.argument("envvar", type=click.STRING, autocompletion=get_env_vars) def cmd1(envvar): click.echo('Environment variable: %s' % envvar) click.echo('Value: %s' % os.environ[envvar])
在 ZSH
中,還支援補全幫助資訊。只需將 autocompletion
回撥函式中返回的字串列表中的字串改為二元元組,第一個元素是補全內容,第二個元素是幫助資訊。
這裡有一個顏色補全的示例:
import os
def get_colors(ctx, args, incomplete):
colors = [('red', 'help string for the color red'),
('blue', 'help string for the color blue'),
('green', 'help string for the color green')]
return [c for c in colors if incomplete in c[0]]
@click.command()
@click.argument("color", type=click.STRING, autocompletion=get_colors)
def cmd1(color):
click.echo('Chosen color is %s' % color)
2.1.2 啟用補全
要啟用 Bash 的補全功能,就需要告訴它你的命令列程式有補全的能力。通常通過一個神奇的環境變數 _<PROG_NAME>_COMPLETE
來告知,其中 <PROG_NAME>
是大寫下劃線形式的程式名稱。
比如有一個命令列程式叫做 foo-bar
,那麼對應的環境變數名稱為 _FOO_BAR_COMPLETE
,然後在 .bashrc
中使用 source
匯出即可:
eval "$(_FOO_BAR_COMPLETE=source foo-bar)"
或者在 .zshrc
中使用:
eval "$(_FOO_BAR_COMPLETE=source_zsh foo-bar)"
不過上面的方式總是在命令列程式啟動時呼叫,這可能在有多個程式時減慢 shell 啟用的速度。另一種方式是把命令放在檔案中,就像這樣:
# 針對 Bash
_FOO_BAR_COMPLETE=source foo-bar > foo-bar-complete.sh
# 針對 ZSH
_FOO_BAR_COMPLETE=source_zsh foo-bar > foo-bar-complete.sh
然後把指令碼檔案路徑加到 .bashrc
或 .zshrc
中:
. /path/to/foo-bar-complete.sh
2.2 實用工具
2.2.1 列印到標準輸出
echo() 函式可以說是最有用的實用工具了。它和 Python 的 print
類似,主要的區別在於它同時在 Python 2 和 3 中生效,能夠智慧地檢測未配置正確的輸出流,且幾乎不會失敗(除了 Python 3 中的少數限制。)
echo
即支援 unicode,也支援二級制資料,如:
import click
click.echo('Hello World!')
click.echo(b'\xe2\x98\x83', nl=False) # nl=False 表示不輸出換行符
2.2.2 ANSI 顏色
有些時候你可能希望輸出是有顏色的,這尤其在輸出錯誤資訊時有用,而 click
在這方面支援的很好。
首先,你需要安裝 colorama
:
pip install colorama
然後,就可以使用 style() 函式來指定顏色:
import click
click.echo(click.style('Hello World!', fg='green'))
click.echo(click.style('Some more text', bg='blue', fg='white'))
click.echo(click.style('ATTENTION', blink=True, bold=True))
click
還提供了更加簡便的函式 secho,它就是 echo
和 style
的組合:
click.secho('Hello World!', fg='green')
click.secho('Some more text', bg='blue', fg='white')
click.secho('ATTENTION', blink=True, bold=True)
2.2.3 分頁支援
有些時候,命令列程式會輸出長文字,但你希望能讓使用者盤也瀏覽。使用 echo_via_pager() 函式就可以輕鬆做到。
例如:
def less():
click.echo_via_pager('\n'.join('Line %d' % idx
for idx in range(200)))
如果輸出的文字特別大,處於效能的考慮,希望翻頁時生成對應內容,那麼就可以使用生成器:
def _generate_output():
for idx in range(50000):
yield "Line %d\n" % idx
@click.command()
def less():
click.echo_via_pager(_generate_output())
2.2.4 清除螢幕
使用 clear() 可以輕鬆清除螢幕內容:
import click
click.clear()
2.2.5 從終端獲取字元
通常情況下,使用內建函式 input
或 raw_input
獲得的輸入是使用者輸出一段字元然後回車得到的。但在有些場景下,你可能想在使用者輸入單個字元時就能獲取到並且做一定的處理,這個時候 getchar() 就派上了用場。
比如,根據輸入的 y
或 n
做特定處理:
import click
click.echo('Continue? [yn] ', nl=False)
c = click.getchar()
click.echo()
if c == 'y':
click.echo('We will go on')
elif c == 'n':
click.echo('Abort!')
else:
click.echo('Invalid input :(')
2.2.6 等待按鍵
在 Windows 的 cmd 中我們經常看到當執行完一個命令後,提示按下任意鍵退出。通過使用 pause() 可以實現暫停直至使用者按下任意鍵:
import click
click.pause()
2.2.7 啟動編輯器
通過 edit() 可以自動啟動編輯器。這在需要使用者輸入多行內容時十分有用。
在下面的示例中,會啟動預設的文字編輯器,並在裡面輸入一段話:
import click
def get_commit_message():
MARKER = '# Everything below is ignored\n'
message = click.edit('\n\n' + MARKER)
if message is not None:
return message.split(MARKER, 1)[0].rstrip('\n')
edit()
函式還支援開啟特定檔案,比如:
import click
click.edit(filename='/etc/passwd')
2.2.8 啟動應用程式
通過 launch 可以開啟 URL 或檔案型別所關聯的預設應用程式。如果設定 locate=True
,則可以啟動檔案管理器並自動選中特定檔案。
示例:
# 開啟瀏覽器,訪問 URL
click.launch("https://click.palletsprojects.com/")
# 使用預設應用程式開啟 txt 檔案
click.launch("/my/downloaded/file.txt")
# 開啟檔案管理器,並自動選中 file.txt
click.launch("/my/downloaded/file.txt", locate=True)
2.2.9 顯示進度條
click
內建了 progressbar() 函式來方便地顯示進度條。
它的用法也很簡單,假定你有一個要處理的可迭代物件,處理完每一項就要輸出一下進度,那麼就有兩種用法。
用法一:使用 progressbar
構造出 bar
物件,迭代 bar
物件來自動告知進度:
import time
import click
all_the_users_to_process = ['a', 'b', 'c']
def modify_the_user(user):
time.sleep(0.5)
with click.progressbar(all_the_users_to_process) as bar:
for user in bar:
modify_the_user(user)
用法二:使用 progressbar
構造出 bar
物件,迭代原始可迭代物件,並不斷向 bar
更新進度:
import time
import click
all_the_users_to_process = ['a', 'b', 'c']
def modify_the_user(user):
time.sleep(0.5)
with click.progressbar(all_the_users_to_process) as bar:
for user in enumerate(all_the_users_to_process):
modify_the_user(user)
bar.update(1)
2.2.10 更多實用工具
- 列印檔名
- 標準流
- 智慧開啟檔案
- 查詢應用程式資料夾
三、總結
click
提供了非常多的增強型功能,本文著重介紹了它的 Bash 補全和十多個實用工具,這會讓你在實現命令列的過程中如虎添翼。此外,click
還提供了諸如命令別名、引數修改、標準化令牌、呼叫其他命令、回撥順序等諸多高階模式 以應對更加複雜或特定的場景,我們就不再深入介紹。
click
的介紹就告一段落,它將會是你編寫命令列程式的一大利器。在下一篇文章中,我們依然會通過實現一個簡單的 git
程式來進行 click
的實戰。
『講解開源專案系列』——讓對開源專案感興趣的人不再畏懼、讓開源專案的發起者不再孤單。跟著我們的文章,你會發現程式設計的樂趣、使用和發現參與開源專案如此簡單。歡迎留言聯絡我們、加入我們,讓更多人愛上開源、貢獻開源