原文参考Build a REPL With Python Prompt Toolkit,很喜欢这篇教程,清晰简单,读起来十分舒服,翻译过来分析给大家

前言

正式开始翻译之前,我们先了解一些背景知识

python-prompt-toolkit

python-prompt-toolkit是一个用于构建强大交互式命令行的 Python 工具库,许多好用而智能的命令行工具都构建在它上边

  • ipython
  • ptpython
  • mycli
  • pgcli
  • xonsh
  • gitsome
  • http-prompt
  • aws-shell
  • haxor-news

如果你用过其中的一些,你会惊叹于他们的智能和良好的交互式体验(就像fish), 这些工具的背后便是我们今天的主角:python-prompt-toolkit

REPL

全称Read-Eval-Print Loop(“读取-求值-输出"循环),是一个简单的,交互式的编程环境.

REPL使得探索性的编程和调试更加便捷,因为“读取-求值-输出”循环通常会比经典的“编辑-编译-运行-调试”模式要更快

译文

本文教你使用prompt_toolkit来构建SQLite数据库的命令行工具

prompt_toolkit是一个用于构建强大交互式命令行的 Python 工具库

首先让我们用pip安装它:

pip install prompt_toolkit

搞起!

读取用户输入

使用prompt方法接收用户输入

1
2
3
4
5
6
7
8
9
from __future__ import unicode_literals
from prompt_toolkit import prompt

def main():
    text = prompt("> ")
    print('You entered:', text)

if __name__ == '__main__':
    main()

REPL循环

现在让我们在一个while循环里调用prompt方法,同时将输入历史保存在InMemoryHistory对象里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from __future__ import unicode_literals
from prompt_toolkit import prompt
from prompt_toolkit.history import InMemoryHistory

def main():
    history = InMemoryHistory()

    while True:
        text = prompt("> ", history=history)
        print('You entered:', text)
    print('GoodBye!')

if __name__ == '__main__':
    main()

语法高亮

我们已经做了一些基础工作,现在让我们将语法高亮加入到用户的输入中。我们知道用户将会输入sql语句,我们可以使用Pygments来高亮用户的输入.lexer参数允许你设置词法分析器(译者注:lexical analyzer,简称lexer),我们将使用Pygments 库的SqlLexer来对输入做高亮处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from __future__ import unicode_literals
from prompt_toolkit import prompt
from prompt_toolkit.history import InMemoryHistory
from pygments.lexers import SqlLexer

def main():
    history = InMemoryHistory()

    while True:
        text = prompt('> ', lexer=SqlLexer, history=history)
        print('You entered:', text)
    print('GoodBye!')

if __name__ == '__main__':
    main()

自动补全

语法高亮屌爆啦!你造什么更牛一些吗?自动补全!开干!

使用WordCompleter类创建一个叫做sql_completer的实例,初始化的时候,定义一组关键字集合,它们将用于自动补全

sql_completer实例将被传递到prompt函数里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from __future__ import unicode_literals
from prompt_toolkit import prompt
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.contrib.completers import WordCompleter
from pygments.lexers import SqlLexer

sql_completer = WordCompleter(['create', 'select', 'insert', 'drop',
                               'delete', 'from', 'where', 'table'], ignore_case=True)


def main():
    history = InMemoryHistory()

    while True:
        text = prompt('> ', lexer=SqlLexer, completer=sql_completer, history=history)
        print('You entered:', text)
    print('GoodBye!')

if __name__ == '__main__':
    main()

大约用了30行代码我们就得到了一个拥有自动补全和语法高亮的REPL。 再接再厉!

修改菜单样式

自动补全菜单忒丑,让我们给它点"颜色"看看!创建DocumentStyle,使它成为pygments.style的子类. 定制自动补全菜单的颜色,并把样式作为参数传到prompt函数里:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from __future__ import unicode_literals
from prompt_toolkit import prompt
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.contrib.completers import WordCompleter
from pygments.lexers import SqlLexer
from pygments.style import Style
from pygments.token import Token
from pygments.styles.default import DefaultStyle

sql_completer = WordCompleter(['create', 'select', 'insert', 'drop',
                               'delete', 'from', 'where', 'table'], ignore_case=True)

class DocumentStyle(Style):
    styles = {
        Token.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000',
        Token.Menu.Completions.Completion: 'bg:#008888 #ffffff',
        Token.Menu.Completions.ProgressButton: 'bg:#003333',
        Token.Menu.Completions.ProgressBar: 'bg:#00aaaa',
    }
    styles.update(DefaultStyle.styles)

def main():
    history = InMemoryHistory()

    while True:
        text = prompt('> ', lexer=SqlLexer, completer=sql_completer,
                      style=DocumentStyle, history=history)
        print('You entered:', text)
    print('GoodBye!')

if __name__ == '__main__':
    main()

剩下的工作就是如何对接到sqlite后端,作为读者的家庭作业好了。哈哈开玩笑啦…我们继续

对接到Sqlite

这一步是可选的 ;) 之前我们一直在关注如何构建REPL. 现在是时候将输入传给SQLite

显然我没有考虑错误处理,但它或许可以作为你们的起步参考

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from __future__ import unicode_literals
import sys
import sqlite3

from prompt_toolkit import prompt, AbortAction
from prompt_toolkit.history import InMemoryHistory
from prompt_toolkit.contrib.completers import WordCompleter
from pygments.lexers import SqlLexer
from pygments.style import Style
from pygments.token import Token
from pygments.styles.default import DefaultStyle

sql_completer = WordCompleter(['create', 'select', 'insert', 'drop',
                               'delete', 'from', 'where', 'table'], ignore_case=True)

class DocumentStyle(Style):
    styles = {
        Token.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000',
        Token.Menu.Completions.Completion: 'bg:#008888 #ffffff',
        Token.Menu.Completions.ProgressButton: 'bg:#003333',
        Token.Menu.Completions.ProgressBar: 'bg:#00aaaa',
    }
    styles.update(DefaultStyle.styles)

def main(database):
    history = InMemoryHistory()
    connection = sqlite3.connect(database)

    while True:
        try:
            text = prompt('> ', lexer=SqlLexer, completer=sql_completer,
                          style=DocumentStyle, history=history,
                          on_abort=AbortAction.RETRY)
        except EOFError:
            break  # Control-D pressed.
        with connection:
            messages = connection.execute(text)
            for message in messages:
                print(message)
    print('GoodBye!')

if __name__ == '__main__':
    if len(sys.argv) < 2:
        db = ':memory:'
    else:
        db = sys.argv[1]

    main(db)

希望这篇文章能给你一些启发去构建命令行工具

以上

附录

unicode

在python2中使用from __future__ import unicode_literals,这样一来所有字符串将被解释为unicode,无论是否有u'',这样一来和python3保持了一致

example

python-prompt-toolkit的教程写得极好,通过examples,花很短的时间,我们就能学会如何使用python-prompt-toolkit的丰富特性