Python readline,使用Cmd接口进行TAB补全循环
我在用Python的cmd.Cmd
类来给我的程序提供一个简单的命令行输入界面。
这是一个完整的例子:
from cmd import Cmd
class CommandParser(Cmd):
def do_x(self, line):
pass
def do_xy(self, line):
pass
def do_xyz(self, line):
pass
if __name__ == "__main__":
parser = CommandParser()
parser.cmdloop()
按两次Tab键会显示可选项。再按一次Tab键也是一样的效果。
我想问的是,怎么让选项在第三次按Tab键时循环显示?在命令行的术语中,我觉得这叫做Tab: menu-complete
,但我不知道怎么把这个应用到Cmd
实例上。
我已经尝试过:
readline.parse_and_bind('Tab: menu-complete')
在创建解析器实例之前和之后都试过,但都没有成功。
我还试过把"Tab: menu-complete"
传给Cmd
的构造函数,但也没成功。
有没有人知道该怎么做?
谢谢!
2 个回答
很遗憾,看来解决这个问题的唯一办法就是对 cmdloop
方法进行“猴子补丁”,也就是修改 cmd.Cmd
类的方法,或者自己写一个。
正确的做法是使用 "Tab: menu-complete"
,但这个设置在类中被覆盖了,具体在第115行:readline.parse_and_bind(self.completekey+": complete")
,所以它从来没有被激活过。(关于第115行和整个 cmd
包的内容,可以查看这个链接:https://hg.python.org/cpython/file/2.7/Lib/cmd.py)。我在下面展示了这个函数的修改版本,以及如何使用它:
import cmd
# note: taken from Python's library: https://hg.python.org/cpython/file/2.7/Lib/cmd.py
def cmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
the remainder of the line as argument.
"""
self.preloop()
if self.use_rawinput and self.completekey:
try:
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey+": menu-complete") # <---
except ImportError:
pass
try:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
if self.use_rawinput:
try:
line = raw_input(self.prompt)
except EOFError:
line = 'EOF'
else:
self.stdout.write(self.prompt)
self.stdout.flush()
line = self.stdin.readline()
if not len(line):
line = 'EOF'
else:
line = line.rstrip('\r\n')
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
if self.use_rawinput and self.completekey:
try:
import readline
readline.set_completer(self.old_completer)
except ImportError:
pass
# monkey-patch - make sure this is done before any sort of inheritance is used!
cmd.Cmd.cmdloop = cmdloop
# inheritance of the class with the active monkey-patched `cmdloop`
class MyCmd(cmd.Cmd):
pass
一旦你对类的方法进行了“猴子补丁”(或者实现了自己的类),它就会提供正确的功能(虽然没有高亮和反向制表,但这些可以根据需要用其他按键来实现)。
readline.parse_and_bind("tab: menu-complete : complete")
最简单的办法是在 menu-complete
后面加一个空格:
parser = CommandParser(completekey="tab: menu-complete ")
然后执行的绑定表达式会变成
readline.parse_and_bind(self.completekey+": complete")
在第二个空格之后的内容其实会被忽略,所以这和 tab: menu-complete
是一样的。
如果你不想依赖这种 readline 解析的行为(我没见过相关文档),你可以使用一个不允许被扩展为 completekey 的 str
子类:
class stubborn_str(str):
def __add__(self, other):
return self
parser = CommandParser(completekey=stubborn_str("tab: menu-complete"))
self.completekey+": complete"
现在和 self.completekey
是一样的。