这是我关于具有优秀命令行用户界面的终端应用程序的两部分系列中的第二部分。在第一篇中,我讨论了使命令行应用程序成为一种纯粹的使用乐趣的特性。在第二部分,我将看看如何在一些库的帮助下在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日在俄勒冈州波特兰市举行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。