這是我關於具有優秀命令列使用者介面的終端應用程式的兩部分系列中的第二部分。在第一篇中,我討論了使命令列應用程式成為一種純粹的使用樂趣的特性。在第二部分,我將看看如何在一些庫的幫助下在Python中實現這些功能。在本文結束時,讀者應該對如何使用 Prompt Toolkit、Click (Command Line Interface Creation Kit)、Pygments 和 Fuzzy Finder 來實現一個易於使用的 REPL有了充分的瞭解。
我計劃在不到20行的Python程式碼中實現這一目標。讓我們開始吧。
Python Prompt Toolkit
我喜歡把這個庫看作是命令列應用程式的瑞士軍刀--它可以替代readline、curses,以及更多的東西。讓我們來安裝這個庫並開始使用。
pip install prompt_toolkit
我們將從一個簡單的REPL開始。一般來說,REPL會接受使用者輸入,進行操作,並列印結果。在我們的例子中,我們將建立一個 "echo "的 REPL。它只是列印回使用者輸入的內容。
REPL
from prompt_toolkit import prompt
while 1:
user_input = prompt('>')
print(user_input)
這就是實現一個REPL的全部內容。它可以讀取使用者的輸入並列印出他們所輸入的內容。這個程式碼片段中使用的prompt函式來自prompt_toolkit庫;它是readline庫的替代品。
歷史
為了加強我們的REPL,我們可以新增命令歷史。
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
while 1:
user_input = prompt(
'>',
history=FileHistory('history.txt'),
)
print(user_input)
我們剛剛在 REPL 中新增了持久化歷史。現在我們可以使用上/下箭頭來瀏覽歷史,並使用Ctrl+R來搜尋歷史。這滿足了命令列的基本禮節。
自動建議
我在第一部分中談到的可發現性技巧之一是自動建議歷史上的命令。(我們在fish shell中看到了這個功能。)讓我們把這個功能新增到我們的REPL中。
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
while 1:
user_input = prompt(
'>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
)
print(user_input)
我們所要做的就是在prompt()的API呼叫中新增一個新引數。現在我們有了一個REPL,它具有魚式的從歷史中自動建議的功能。
自動完成
現在讓我們通過自動完成來實現對Tab鍵的增強,當使用者開始輸入時,它將彈出可能的建議。
我們的 REPL 如何知道該建議什麼?我們提供一個可能建議的專案的字典。
比方說,我們正在實現一個SQL的REPL。我們可以在我們的自動完成字典中儲存SQL關鍵字。讓我們看看如何做到這一點。
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
ignore_case=True)
while 1:
user_input = prompt(
'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter,
)
print(user_input)
再一次,我們可以簡單地使用prompt-toolkit的內建完成程式,稱為WordCompleter,它將使用者輸入與可能的建議字典相匹配,並提供一個列表。
我們現在有了一個 REPL,它可以進行自動補全,從歷史中獲取魚式建議,並對歷史進行上/下遍歷。所有這些都在不到10行的實際程式碼中完成。
Click
Click是一個命令列建立工具包,它可以很容易地解析命令列選項引數和程式引數。本節不談如何將Click作為引數解析器使用;相反,我將看一下Click附帶的一些實用程式。
安裝Click很簡單。
pip install click
尋呼機
尋呼機是Unix的實用工具,每次顯示一頁長的輸出。呼叫器的例子有:less, more, most, 等等。通過尋呼機顯示命令的輸出,不僅是友好的設計,而且也是體面的做法。
讓我們進一步看看前面的例子。我們可以使用click.echo_via_pager(),而不是使用預設的print()語句。這將負責通過一個分頁器將輸出傳送到stdout。它是與平臺無關的,所以它可以在Unix或Windows中工作。click.echo_via_pager()將嘗試使用合適的預設值,以便在必要時能夠顯示顏色程式碼。
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
import click
SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
ignore_case=True)
while 1:
user_input = prompt(
'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter,
)
click.echo_via_pager(user_input)
編輯
我在上一篇文章中提到的一個好處是,當命令變得太複雜時,可以返回到編輯器。click再次提供了一個簡單的API來啟動一個編輯器,並將在編輯器中輸入的文字返回給應用程式。
輸入click
message = click.edit()
Fuzzy Finder
Fuzzy Finder是一種讓使用者用最少的輸入來縮小建議範圍的方法。再一次,有一個庫實現了Fuzzy Finder。讓我們來安裝這個庫。
pip install fuzzyfinder
Fuzzy Finder的API很簡單。你傳入部分字串和一個可能的選擇列表,Fuzzy Finder將返回一個新的列表,該列表使用模糊演算法按相關性排序與部分字串相匹配。比如說
>>> from fuzzyfinder.main import fuzzyfinder
>> suggestions = fuzzyfinder('abc', ['abcd', 'defabca', 'aagbec', 'xyz', 'qux'] )
>> list(sustips)
['abcd', 'defabca', 'aagbec']
現在我們有了我們的fuzzyfinder,讓我們把它新增到我們的SQL REPL中。我們這樣做的方法是定義一個自定義的完成器,而不是prompt-toolkit附帶的**WordCompleter。比如說
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import Completer, Completion
import click
from fuzzyfinder.main import fuzzyfinder
SQLKeywords = ['select', 'from', 'insert', 'update', 'delete', 'drop']
class SQLCompleter(Completer):
def get_completions(self, document, complete_event):
word_before_cursor = document.get_word_before_cursor(WORD=True)
matches = fuzzyfinder(word_before_cursor, SQLKeywords)
for m in matches:
yield Completion(m, start_position=-len(word_before_cursor))
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter(),
)
click.echo_via_pager(user_input)
Pygments
現在讓我們為使用者的輸入新增語法高亮。我們正在建立一個SQL REPL,有彩色的SQL語句會很好。
Pygments是一個語法高亮庫,內建支援超過300種語言。新增語法高亮使應用程式變得豐富多彩,這有助於使用者在執行SQL之前發現錯誤--如錯別字、不匹配的引號或括號。
首先安裝Pygments。
pip install pygments
讓我們使用Pygments為我們的SQL REPL新增顏色。
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
從 prompt_toolkit.auto_suggest 匯入 AutoSuggestFromHistory
從 prompt_toolkit.completion 匯入 Completer, Completion
匯入點選
從 fuzzyfinder 匯入 fuzzyfinder
from pygments.lexers.sql import SqlLexer
SQLKeywords = ['選擇', '來自', '插入', '更新', '刪除', '放棄']
class SQLCompleter(Completer):
def get_completions(self, document, complete_event):
word_before_cursor = document.get_word_before_cursor(WORD=True)
matches = fuzzyfinder(word_before_cursor, SQLKeywords)
for m in matches:
產量完成(m, start_position=-len(word_before_cursor))
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter(),
lexer=SqlLexer,
)
click.echo_via_pager(user_input)
Prompt Toolkit與Pygments庫配合良好。我們選擇Pygments提供的SqlLexer,並將其傳入prompt-toolkit的prompt** API。現在,所有的使用者輸入都被視為SQL語句,並被適當地著色。
結語
這就結束了我們建立一個強大的REPL的旅程,它具有普通shell的所有功能,如歷史、鍵繫結,以及使用者友好的功能,如自動補全、模糊查詢、呼叫器支援、編輯器支援和語法突出顯示。我們在不到20條Python語句中實現了所有這些。
這不是很容易嗎?現在你沒有藉口不寫一個出色的命令列應用程式了。這些資源可能會有所幫助。
- Click (命令列介面建立工具包)
- Fuzzy Finder
- Prompt Toolkit
- 參見prompt-toolkit資源庫中的Prompt Toolkit tutorial教程和例項。
- Pygments
*在Amjith Ramanujam的PyCon US 2017講座中瞭解更多資訊,Awesome Commandline Tools,5月20日在俄勒岡州波特蘭市舉行。