15.4. readline — GNU readline 类库
目的:提供 GNU readline 库的接口,以便在命令提示符下与用户交互。
readline
模块可用于增强交互式命令行程序,使其更易于使用。 它主要用于提供命令行文本完成或「选项卡完成」。
注意
因为
readline
与控制台内容交互,所以打印调试消息使得很难看到示例代码中发生了什么,而不是 readline 正在做什么。 以下示例使用logging
模块将调试信息写入单独的文件。 每个示例都显示了日志输出。注意
默认情况下,
readline
所需的 GNU 库并非在所有平台上都可用。 如果你的系统不包含它们,则可能需要在安装依赖项后重新编译 Python 解释器以启用该模块。 该库的独立版本也在 Python 包索引的以名称 gnureadline 分发。 本节中的示例首先尝试导入 gnureadline ,然后再回到 readline 。特别感谢 Jim Baker 告知这个包。
配置
使用配置文件或 parse_and_bind()
函数,有两种方法可以配置基础 readline 库。 配置选项包括用于调用完成的键绑定,编辑模式( vi
或 emacs
)以及许多其他值。 有关详细信息,请参阅 GNU readline 库的文档。
启用制表符完成的最简单方法是调用 parse_and_bind()
。 其他选项可以同时设置。 此示例将编辑控件更改为使用「 vi 」模式而不是默认的「 emacs 」。 要编辑当前输入行,请按「 ESC 」然后使用普通的 vi
导航键,例如 j
, k
, l
和 h
。
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
则假定没有匹配的完成。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。