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 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/readline...

译文地址:https://learnku.com/docs/pymotw/readline...

上一篇 下一篇
讨论数量: 0
发起讨论 查看所有版本


暂无话题~