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