作者: Amjith Ramanujam 译者: LCTT Lv Feng

在这个分为两篇的关于具有绝佳命令行界面的终端程序的系列文章的第二篇教程中,我们将讨论 Prompt、Toolkit、Click、Pygments 和 Fuzzy Finder 。

这是我的一个分为两篇的关于具有绝佳命令行界面的终端程序的系列文章的第二篇教程。在第一篇文章中,我们讨论了一些能够使命令行应用用起来令人感到愉悦的特性。在第二篇文章中,我们来看看如何用 Python 的一些库来实现这些特性。

我打算用少于 20 行 Python 代码来实现。让我们开始吧。

Python Prompt Toolkit

我习惯于把这个库称为命令行应用的瑞士军刀,它可以作为 readline 、curses 等的替代品。让我们首先安装这个库,然后开始该教程:

  1. pip install prompt_toolkit

我们以一个简单的 REPL (LCTT 译注:REPL —— Read-Eval-Print Loop,交互式开发环境)开始。一个典型的 REPL 会接收用户的输入,进行一个操作,然后输出结果。比如在我们的例子中,我们将要实现一个具有 “回显” 功能的 REPL 。它仅仅是原样打印出用户的输入:

REPL

  1. from prompt_toolkit import prompt
  2. while 1:
  3. user_input = prompt('>')
  4. print(user_input)

这就是实现 REPL 的全部代码。它可以读取用户的输入,然后打印出用户的输入内容。在这段代码中使用的 prompt 函数来自 prompt_toolkit 库,它是 readline 库的一个替代品。

命令历史

为了增强我们的 REPL 的功能,我们可以添加命令历史:

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. while 1:
  4. user_input = prompt('>',
  5. history=FileHistory('history.txt'),
  6. )
  7. print(user_input)

我们刚刚给 REPL 添加了持久的命令历史。现在,我们可以使用上/下箭头来浏览命令历史,并使用 Ctrl-R 来搜索命令历史。它满足了命令行的基本准则。

自动推荐

在第一篇教程中,我讲到的一个可发现性技巧是自动推荐历史命令。(我是首先在 fish shell 中看到的这一特性)让我们把这一特性加入到我们的 REPL 中:

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
  4. while 1:
  5. user_input = prompt('>',
  6. history=FileHistory('history.txt'),
  7. auto_suggest=AutoSuggestFromHistory(),
  8. )
  9. print(user_input)

我们只需要给 prompt() API 调用添加一个新的参数。现在,我们有了一个具有 fish shell 风格的 REPL,它可以自动推荐历史命令。

自动补全

现在,让我们通过自动补全来加强 Tab 补全。它能够在用户开始输入的时候弹出可能的命令推荐。

REPL 如何来进行推荐呢?我们使用一个字典来进行可能项的推荐。

比如说我们实现一个针对 SQL 的 REPL 。我们可以把 SQL 关键字存到自动补全字典里面。让我们看一看这是如何实现的:

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
  4. from prompt_toolkit.contrib.completers import WordCompleter
  5. SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
  6. ignore_case=True)
  7. while 1:
  8. user_input = prompt('SQL>',
  9. history=FileHistory('history.txt'),
  10. auto_suggest=AutoSuggestFromHistory(),
  11. completer=SQLCompleter,
  12. )
  13. print(user_input)

再次说明,我们只是简单的使用了 prompt-toolkit 内建的一个叫做 WordCompleter 的补全特性,它能够把用户输入和可能推荐的字典进行匹配,然后提供一个列表。

现在,我们有了一个能够自动补全、fish shell 风格的历史命令推荐以及上/下浏览历史的 REPL 。实现这些特性只用了不到 10 行的实际代码。

Click

Click 是一个命令行创建工具包,使用它能够更容易的为程序解析命令行选项的参数和常量。在这儿我们不讨论如何使用 Click 来作为参数解析器。相反,我们将会看看 Click 带有的一些功能。

安装 Click

  1. pip install click

分页器

分页器是 Unix 系统上的实用工具,它们能够一次一页地显示很长的输出。分页器的一些例子包括 lessmoremost 等。通过分页器来显示一个命令的输出不仅仅是一个友好的设计,同时也是必要的。

让我们进一步改进前面的例子。我们不再使用默认的 print() 语句,取而代之的是 click.echo_via_pager() 。它将会把输出通过分页器发送到标准输出。这是平台无关的,因此在 Unix 系统或 Windows 系统上均能工作。如果必要的话,click_via_pager 会尝试使用一个合适的默认分页器来输出,从而能够显示代码高亮。

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
  4. from prompt_toolkit.contrib.completers import WordCompleter
  5. import click
  6. SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
  7. ignore_case=True)
  8. while 1:
  9. user_input = prompt(u'SQL>',
  10. history=FileHistory('history.txt'),
  11. auto_suggest=AutoSuggestFromHistory(),
  12. completer=SQLCompleter,
  13. )
  14. click.echo_via_pager(user_input)

编辑器

在我前面的文章中一个值得一提的细节是,当命令过于复杂的时候进入编辑器来编辑。Click 有一个简单的 API 能够打开编辑器,然后把在编辑器中输入的文本返回给应用。

  1. import click
  2. message = click.edit()

Fuzzy Finder

Fuzzy Finder 是一种通过少量输入来为用户减少推荐的方法。幸运的是,有一个库可以实现 Fuzzy Finder 。让我们首先安装这个库:

  1. pip install fuzzyfinder

Fuzzy Finder 的 API 很简单。用户向它传递部分字符串和一系列可能的选择,然后,Fuzzy Finder 将会返回一个与部分字符串匹配的列表,这一列表是通过模糊算法根据相关性排序得出的。比如:

  1. >>> from fuzzyfinder import fuzzyfinder
  2. >>> suggestions = fuzzyfinder('abc', ['abcd', 'defabca', 'aagbec', 'xyz', 'qux'])
  3. >>> list(suggestions)
  4. ['abcd', 'defabca', 'aagbec']

现在我们有了 fuzzyfinder,让我们把它加入到我们的 SQL REPL 中。方法是我们自定义一个 completer 而不是使用来自 prompt-toolkit 库的 WordCompleter 。比如:

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
  4. from prompt_toolkit.completion import Completer, Completion
  5. import click
  6. from fuzzyfinder import fuzzyfinder
  7. SQLKeywords = ['select', 'from', 'insert', 'update', 'delete', 'drop']
  8. class SQLCompleter(Completer):
  9. def get_completions(self, document, complete_event):
  10. word_before_cursor = document.get_word_before_cursor(WORD=True)
  11. matches = fuzzyfinder(word_before_cursor, SQLKeywords)
  12. for m in matches:
  13. yield Completion(m, start_position=-len(word_before_cursor))
  14. while 1:
  15. user_input = prompt(u'SQL>',
  16. history=FileHistory('history.txt'),
  17. auto_suggest=AutoSuggestFromHistory(),
  18. completer=SQLCompleter(),
  19. )
  20. click.echo_via_pager(user_input)

Pygments

现在,让我们给用户输入添加语法高亮。我们正在搭建一个 SQL REPL,如果具有彩色高亮的 SQL 语句,这会很棒。

Pygments 是一个提供语法高亮的库,内建支持超过 300 种语言。添加语法高亮能够使应用变得彩色化,从而能够帮助用户在执行程序前发现 SQL 中存在的错误,比如拼写错误、引号不匹配或括号不匹配。

首先,安装 Pygments :

  1. pip install pygments

让我们使用 Pygments 来为 SQL REPL 添加颜色:

  1. from prompt_toolkit import prompt
  2. from prompt_toolkit.history import FileHistory
  3. from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
  4. from prompt_toolkit.completion import Completer, Completion
  5. import click
  6. from fuzzyfinder import fuzzyfinder
  7. from pygments.lexers.sql import SqlLexer
  8. SQLKeywords = ['select', 'from', 'insert', 'update', 'delete', 'drop']
  9. class SQLCompleter(Completer):
  10. def get_completions(self, document, complete_event):
  11. word_before_cursor = document.get_word_before_cursor(WORD=True)
  12. matches = fuzzyfinder(word_before_cursor, SQLKeywords)
  13. for m in matches:
  14. yield Completion(m, start_position=-len(word_before_cursor))
  15. while 1:
  16. user_input = prompt(u'SQL>',
  17. history=FileHistory('history.txt'),
  18. auto_suggest=AutoSuggestFromHistory(),
  19. completer=SQLCompleter(),
  20. lexer=SqlLexer,
  21. )
  22. click.echo_via_pager(user_input)

Prompt Toolkit 能够和 Pygments 一同很好的工作。我们把 Pygments 提供的 SqlLexer 加入到来自 prompt-toolkit 的 prompt 中。现在,所有的用户输入都会被当作 SQL 语句,并进行适当着色。

结论

我们的“旅途”通过创建一个强大的 REPL 结束,这个 REPL 具有常见的 shell 的全部特性,比如历史命令,键位绑定,用户友好性比如自动补全、模糊查找、分页器支持、编辑器支持和语法高亮。我们仅用少于 20 行 Python 代码就实现了这个 REPL 。

不是很简单吗?现在,你没有理由不会写一个自己的命令行应用了。下面这些资源可能有帮助:

你也可以在我在 PyCon US 2017 的演讲优秀的命令行工具中学到更多东西,该会议是 5 月 20 日在波特兰,俄勒冈举行的。

4 个用于构建优秀的命令行用户界面的 Python 库的更多相关文章

  1. 如何编写一个带命令行参数的Python文件

    看到别人执行一个带命令行参数的python文件,瞬间觉得高大上起来.牛逼起来,那么如何编写一个带命令行参数的python脚本呢?不用紧张,下面将简单易懂地让你学会如何让自己的python脚本,支持带命 ...

  2. 一个简单、易用的Python命令行(terminal)进度条库

    eprogress 是一个简单.易用的基于Python3的命令行(terminal)进度条库,可以自由选择使用单行显示.多行显示进度条或转圈加载方式,也可以混合使用. 示例 单行进度条 多行进度条 圆 ...

  3. 区分命令行模式和Python交互模式

    命令行模式 在Windows开始菜单选择"命令提示符",就进入到命令行模式,它的提示符类似C:\> Python交互模式 在命令行模式下敲命令python,就看到类似如下的一 ...

  4. 命令行下查看python和numpy的版本和安装位置

    命令行下查看python和numpy的版本和安装位置 1.查看python版本 方法一: python -V 注意:‘-V‘中‘V’为大写字母,只有一个‘-’ 方法二: python --versio ...

  5. 命令行模式和python交互模式

    一.命令行模式 在Windows开始菜单选择“命令提示符”,就进入到命令行模式,它的提示符类似C:>:. 二.Python交互模式 在命令行模式下敲命令python,就看到类似如下的一堆文本输出 ...

  6. python如何通过windows命令行运行一个python程序文件?

    python如何通过windows命令行运行一个python程序文件? cmd 进入到py文件对应目录下或者直接在上面的文件地址栏输入cmd,敲入回车 定位到对应的目录下 输入python xxx.p ...

  7. 在命令行中输入python会跳转到商店问题解决,python环境变量的配置

    安装python出了点问题,明明安装了,在应用商店显示已获取,可是在命令行输入python检验时就直接跳转到win10系统自带的应用商店...... 这不免让我怀疑是不是没有安装好python~但是它 ...

  8. 使用Cli构建Go的命令行应用

    转载出处:http://www.opscoder.info/cli.html   在Go里面应用中flag这一标准库,提供了很多我们在写命令行时需要的interface,然而如果你需要更强大更好的结构 ...

  9. 构建一个Flowable命令行应用

    官网链接 [(https://flowable.com/open-source/docs/bpmn/ch02-GettingStarted/#building-a-command-line-appli ...

随机推荐

  1. Centos调整时间时区

    一台VPS的时间出错,使用常规手段修改均失败.提示hwclock failed : ntpdate stdtime.sinica.edu.tw 如果你的 VPS 提示没有 ntpdate 这个命令,可 ...

  2. safari input默认样式

    在i标签下嵌套一个input标签  设置了 -webkit-apprarence:none: 设置了宽高,和padding:3px 结果placeholder显示不全 经排查 这时候的input默认有 ...

  3. warning: already initialized constant FileUtils::VERSION

    Ran into this, and the solution here works: https://stackoverflow.com/questions/51334732/rails-5-2-0 ...

  4. Hadoop MapReduce流程及容错

    shuffle流程 输入分片(input split):在进行map计算之前,mapreduce会根据输入文件计算输入分片(input split),每个输入分片(input split)针对一个ma ...

  5. 使用gitlab, jenkins搭建CI(持续集成)系统(1) -- 准备环境

    1. 环境设计 搭建一个从开发到测试知道发布上线可以自动换完成的CI系统.这个系统中包含4个环境. 开发(dev)环境: 码农使用. 测试(test)环境: 测试人员使用. 预发布(prepublis ...

  6. JAVA-6NIO之FileChannel

    Java NIO中的FileChannel是一个连接到文件的通道.可以通过文件通道读写文件. FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下. 打开FileChannel 在使用F ...

  7. [Codeforces 925C]Big Secret

    Description 题库链接 给出 \(n\) 个数,让你生成这 \(n\) 个数的一个排列 \(A\) .定义 \(B_i = \bigoplus\limits_{j=1}^i A_j\) , ...

  8. asdfasdfasdfasdf

  9. sql中的分页实现

    1.两种方式 1.1:row_number() over()函数 1.2:OFFSET ....FETCH OFFSET { integer_constant | offset_row_count_e ...

  10. c#与IronPython Clojure-clr的调用

    一,python 安装ironpython http://ironpython.net/ 新建控制台程序,引入 IronPython,Microsoft.Scripting 新建xxx.py文件 va ...