In the second tutorial in this two-part series about terminal programs with great command-line interfaces, we'll discuss Prompt, Toolkit, Click, Pygments, and Fuzzy Finder.
This is the second tutorial in my two-part series about terminal programs with great command line interfaces. In the first article, we discussed some of the features that make command line applications a joy to use. In this second article, we’ll look at how to implement these features using some of Python’s libraries.
I plan to implement it in less than 20 lines of Python code. let's start.
Python Prompt ToolkitI am accustomed to calling this library the Swiss Army Knife of command line applications. It can be used as a replacement for readline, curses, etc. Let's first install this library and then start the tutorial:
pip install prompt_toolkit
We start with a simple REPL (LCTT Annotation: REPL - Read-Eval-Print Loop, interactive development environment). A typical REPL receives user input, performs an operation, and outputs the result. For example, in our example, we are going to implement a REPL with "echo" functionality. It simply prints out the user's input as is:
REPLfrom prompt_toolkit import prompt while 1: user_input = prompt('>') print(user_input)
This is all the code to implement REPL. It reads the user's input and then prints out the user's input. The prompt function used in this code comes from the prompt_toolkit library, which is a replacement for the readline library.
Command HistoryTo enhance the functionality of our REPL, we can add command history:
from prompt_toolkit import prompt from prompt_toolkit.history import FileHistory while 1: user_input = prompt('>', history=FileHistory('history.txt'), ) print(user_input)
We just added a persistent command history to the REPL. Now we can use the up/down arrows to browse the command history and Ctrl-R to search the command history. It meets the basic guidelines of the command line.
Automatic recommendationIn the first tutorial, one of the discoverability techniques I talked about was automatically recommending historical commands. (I first saw this feature in fish shell) Let's add this feature to our 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)
We just need to add a new parameter to the prompt() API call. Now we have a fish shell-style REPL that automatically recommends historical commands.
AutocompleteNow, let’s enhance tab completion with autocompletion. It pops up possible command recommendations as the user starts typing.
REPL How to make recommendations? We use a dictionary to recommend possible items.
For example, let’s implement a REPL for SQL. We can store SQL keywords in the auto-complete dictionary. Let's see how this is done:
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)
Again, we simply used prompt-toolkit's built-in completion feature called WordCompleter, which matches user input with possible recommended dictionaries. , and then provide a list.
Now, we have a REPL with auto-completion, fish shell-style historical command recommendations, and up/down browsing history. Implementing these features took less than 10 lines of actual code.
ClickClick is a command line creation toolkit that makes it easier to parse command line option parameters and constants for programs. We will not discuss how to use Click as a parameter parser here. Instead, we'll look at some of the features that Click comes with.
Install Click:
pip install click
Paginators are utilities on Unix systems that are capable of displaying very long output one page at a time. Some examples of pagers include less, more, most, etc. Displaying the output of a command through a pager is not only a friendly design, but also a necessity.
让我们进一步改进前面的例子。我们不再使用默认print()语句,取而代之的是click.echo_via_pager()。它将会把输出通过分页器发送到标准输出。这是平台无关的,因此在 Unix 系统或 Windows 系统上均能工作。如果必要的话,click_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 。让我们首先安装这个库:
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)
现在,让我们给用户输入添加语法高亮。我们正在搭建一个 SQL REPL,如果具有彩色高亮的 SQL 语句,这会很棒。
Pygments是一个提供语法高亮的库,内建支持超过 300 种语言。添加语法高亮能够使应用变得彩色化,从而能够帮助用户在执行程序前发现 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中。现在,所有的用户输入都会被当作 SQL 语句,并进行适当着色。
结论我们的“旅途”通过创建一个强大的 REPL 结束,这个 REPL 具有常见的 shell 的全部特性,比如历史命令,键位绑定,用户友好性比如自动补全、模糊查找、分页器支持、编辑器支持和语法高亮。我们仅用少于 20 行 Python 代码就实现了这个 REPL 。
不是很简单吗?现在,你没有理由不会写一个自己的命令行应用了。
The above is the detailed content of A library for building high-quality Python command-line user interfaces. For more information, please follow other related articles on the PHP Chinese website!