这是我的关于具有出色命令行 UI 的终端应用程序的两部分系列文章的第二部分。在第一篇文章中,我讨论了使命令行应用程序使用起来非常愉悦的功能。在第二部分中,我将介绍如何在 Python 中借助一些库来实现这些功能。在本文结束时,读者应该对如何使用 Prompt Toolkit、Click (命令行界面创建工具包)、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 调用添加一个新参数。现在我们有了一个具有 fish 风格的历史记录自动建议的 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.contrib.completers 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,它将用户输入与可能的建议字典进行匹配,并提供一个列表。
我们现在有了一个可以进行自动完成、fish 风格的历史记录建议以及历史记录向上/向下遍历的 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.contrib.completers import WordCompleter
import click
SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
ignore_case=True)
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter,
)
click.echo_via_pager(user_input)
编辑器
我在上一篇文章中提到的优点之一是,当命令变得太复杂时,可以退回到编辑器。click 再次提供了一个 简单的 API 来启动编辑器,并将编辑器中输入的文本返回给应用程序
import click
message = click.edit()
Fuzzy Finder
Fuzzy Finder 是一种供用户通过最少的输入来缩小建议范围的方法。同样,有一个库实现了 Fuzzy Finder。让我们安装这个库
pip install fuzzyfinder
Fuzzy Finder 的 API 很简单。您传入部分字符串和可能的选择列表,Fuzzy Finder 将返回一个新的列表,该列表使用模糊算法匹配部分字符串,并按相关性排序。例如
>>> from fuzzyfinder import fuzzyfinder
>>> suggestions = fuzzyfinder('abc', ['abcd', 'defabca', 'aagbec', 'xyz', 'qux'])
>>> list(suggestions)
['abcd', 'defabca', 'aagbec']
现在我们有了 fuzzyfinder,让我们将其添加到我们的 SQL REPL 中。我们这样做的方式是定义一个自定义的 completer,而不是 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 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 之前发现 SQL 中的错误,例如拼写错误、不匹配的引号或括号。
首先安装 Pygments
pip install pygments
让我们使用 Pygments 为我们的 SQL REPL 添加颜色
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 import fuzzyfinder
from pygments.lexers.sql import SqlLexer
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(),
lexer=SqlLexer,
)
click.echo_via_pager(user_input)
Prompt Toolkit 与 Pygments 库配合良好。我们选择 Pygments 提供的 SqlLexer 并将其传递到 prompt-toolkit 的 prompt API 中。现在所有用户输入都被视为 SQL 语句并进行相应的着色。
结论
本文总结了我们创建功能强大的 REPL 的旅程,该 REPL 具有常见 shell 的所有功能,例如历史记录、按键绑定以及用户友好的功能,例如自动完成、模糊查找、分页器支持、编辑器支持和语法高亮。我们用少于 20 行 Python 语句实现了所有这些功能。
是不是很简单?现在你没有借口不编写出色的命令行应用程序了。这些资源可能会有所帮助
- Click (命令行界面创建工具包)
- Fuzzy Finder
- Prompt Toolkit
- 请参阅 Prompt Toolkit 教程 和 prompt-toolkit 存储库中的 示例。
- Pygments
在 Amjith Ramanujam 的 PyCon US 2017 演讲,Awesome Commandline Tools,5 月 20 日在俄勒冈州波特兰市了解更多信息。
3 条评论