15.4. readline — GNU readline 类库

未匹配的标注

目的:提供 GNU readline 库的接口,以便在命令提示符下与用户交互。

readline 模块可用于增强交互式命令行程序,使其更易于使用。 它主要用于提供命令行文本完成或「选项卡完成」。

注意

因为 readline 与控制台内容交互,所以打印调试消息使得很难看到示例代码中发生了什么,而不是 readline 正在做什么。 以下示例使用logging 模块将调试信息写入单独的文件。 每个示例都显示了日志输出。

注意

默认情况下, readline 所需的 GNU 库并非在所有平台上都可用。 如果你的系统不包含它们,则可能需要在安装依赖项后重新编译 Python 解释器以启用该模块。 该库的独立版本也在 Python 包索引的以名称 gnureadline 分发。 本节中的示例首先尝试导入 gnureadline ,然后再回到 readline 。

特别感谢 Jim Baker 告知这个包。

配置

使用配置文件或 parse_and_bind() 函数,有两种方法可以配置基础 readline 库。 配置选项包括用于调用完成的键绑定,编辑模式( viemacs )以及许多其他值。 有关详细信息,请参阅 GNU readline 库的文档。

启用制表符完成的最简单方法是调用 parse_and_bind() 。 其他选项可以同时设置。 此示例将编辑控件更改为使用「 vi 」模式而不是默认的「 emacs 」。 要编辑当前输入行,请按「 ESC 」然后使用普通的 vi 导航键,例如 jklh

readline_parse_and_bind.py

try:
    import gnureadline as readline
except ImportError:
    import readline

readline.parse_and_bind('tab: complete')
readline.parse_and_bind('set editing-mode vi')

while True:
    line = input('Prompt ("stop" to quit): ')
    if line == 'stop':
        break
    print('ENTERED: {!r}'.format(line))

可以将相同的配置存储为库中通过单个调用读取的文件中的指令。如果 myreadline.rc 包含

myreadline.rc

# 启用 tab 完成
tab: complete

# 使用 vi  编辑模式替代 emacs
set editing-mode vi

文件可以用 read_init_file() 读取

readline_read_init_file.py

try:
    import gnureadline as readline
except ImportError:
    import readline

readline.read_init_file('myreadline.rc')

while True:
    line = input('Prompt ("stop" to quit): ')
    if line == 'stop':
        break
    print('ENTERED: {!r}'.format(line))

完成文本

该程序具有一组内置的可能命令,并在用户输入指令时使用制表符完成。

readline_completer.py

try:
    import gnureadline as readline
except ImportError:
    import readline
import logging

LOG_FILENAME = '/tmp/completer.log'
logging.basicConfig(
    format='%(message)s',
    filename=LOG_FILENAME,
    level=logging.DEBUG,
)

class SimpleCompleter:

    def __init__(self, options):
        self.options = sorted(options)

    def complete(self, text, state):
        response = None
        if state == 0:
            # 这是此文本的第一次,因此建立一个匹配列表。
            if text:
                self.matches = [
                    s
                    for s in self.options
                    if s and s.startswith(text)
                ]
                logging.debug('%s matches: %s',
                              repr(text), self.matches)
            else:
                self.matches = self.options[:]
                logging.debug('(empty input) matches: %s',
                              self.matches)

        # 如果有那么多,请从匹配列表中返回状态项。
        try:
            response = self.matches[state]
        except IndexError:
            response = None
        logging.debug('complete(%s, %s) => %s',
                      repr(text), state, repr(response))
        return response

def input_loop():
    line = ''
    while line != 'stop':
        line = input('Prompt ("stop" to quit): ')
        print('Dispatch {}'.format(line))

# 注册完成功能
OPTIONS = ['start', 'stop', 'list', 'print']
readline.set_completer(SimpleCompleter(OPTIONS).complete)

# 使用 Tab 键完成
readline.parse_and_bind('tab: complete')

# 提示用户输入文字
input_loop()

input_loop() 函数读取一行接一行,直到输入值为 "stop" 。 更复杂的程序实际上可以解析输入行并运行命令。

SimpleCompleter 类保留了一个“选项”列表,它们是自动完成的候选者。 实例的 complete() 方法设计为使用 readline 注册为完成源。 参数是要完成的 text 字符串和 state 值,表示使用相同的文本调用函数的次数。 重复调用该函数,每次递增状态。 如果有该状态值的候选者,它应该返回一个字符串;如果没有更多的候选者,它应该返回 None 。 这里 complete() 的实现在状态为 0 时查找一组匹配,然后在后续调用中一次返回所有候选匹配。

运行时,初始输出为:

$ python3 readline_completer.py

Prompt ("stop" to quit):

按下 TAB 两次会打印一个选项列表。

$ python3 readline_completer.py

Prompt ("stop" to quit):
list   print  start  stop
Prompt ("stop" to quit):

日志文件显示使用两个独立的状态值序列调用 complete()

$ tail -f /tmp/completer.log

(empty input) matches: ['list', 'print', 'start', 'stop']
complete('', 0) => 'list'
complete('', 1) => 'print'
complete('', 2) => 'start'
complete('', 3) => 'stop'
complete('', 4) => None
(empty input) matches: ['list', 'print', 'start', 'stop']
complete('', 0) => 'list'
complete('', 1) => 'print'
complete('', 2) => 'start'
complete('', 3) => 'stop'
complete('', 4) => None

第一个序列来自第一个 TAB 按键。 完成算法要求所有候选者但不扩展空输入行。 然后在第二个 TAB 上,重新计算候选列表,以便为用户打印。

如果下一个输入是「 l 」后跟另一个 TAB ,则屏幕显示:

Prompt ("stop" to quit): list

并且日志反映了 complete() 的不同参数:

'l' matches: ['list']
complete('l', 0) => 'list'
complete('l', 1) => None

现在按回车会导致 input() 返回该值,并且 while 循环。

Dispatch list
Prompt ("stop" to quit):

对于以“s”开头的命令,有两种可能的完成。 键入 s ,然后按 TAB 发现「 start 」和「 stop 」是候选者,但只是通过添加「 t 」部分完成屏幕上的文本。

日志文件显示:

's' matches: ['start', 'stop']
complete('s', 0) => 'start'
complete('s', 1) => 'stop'
complete('s', 2) => None

和屏幕:

Prompt ("stop" to quit): st

注意

如果一个完成函数引发一个异常,它将被静默忽略,而 readline 则假定没有匹配的完成。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
讨论数量: 0
发起讨论 只看当前版本


暂无话题~