【问题标题】:Python readline, tab completion cycling with the Cmd interfacePython readline,使用 Cmd 界面的选项卡完成循环
【发布时间】:2014-08-08 14:47:31
【问题描述】:

我正在使用 Python 中的 cmd.Cmd 类为我的程序提供一个简单的 readline 接口。

自包含示例:

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 也一样。

我的问题是,如何获得在第三次按标签时循环的选项?在 readline 术语中,我认为这称为 Tab: menu-complete,但我看不出如何将其应用于 Cmd 实例。

我已经试过了:

readline.parse_and_bind('Tab: menu-complete')

在实例化解析器实例之前和之后。没有运气。

我还尝试将"Tab: menu-complete" 传递给Cmd 构造函数。这里也没有运气。

有人知道它是怎么做的吗?

干杯!

【问题讨论】:

  • 似乎有一个方法Cmd.complete你可能可以覆盖,但不要问我如何......
  • 这个库看起来不像你想要的,你可以直接使用readline吗?

标签: python readline


【解决方案1】:

不幸的是,似乎唯一的解决方法是对 cmd.Cmd 类中的方法 cmdloop 进行猴子补丁,或者自己动手。

正确的方法是使用"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

一旦您对类方法进行了猴子修补(或实现了您自己的类),它就会提供正确的行为(尽管没有突出显示和反向制表符,但这些可以根据需要使用其他键来实现)。

【讨论】:

  • 谢谢。它使我能够自动完成一些其他方法难以使用的数据,例如完成中的空格。
【解决方案2】:

最简单的技巧是在menu-complete 之后添加一个空格:

parser = CommandParser(completekey="tab: menu-complete ")

执行的绑定表达式

readline.parse_and_bind(self.completekey+": complete")

会变成

readline.parse_and_bind("tab: menu-complete : complete")

第二个空格之后的所有内容都被忽略了,所以它与tab: menu-complete相同。

如果您不想依赖 readline 解析的这种行为(我还没有看到它的文档),您可以使用 str 的子类,它拒绝扩展为 completekey:

class stubborn_str(str):
    def __add__(self, other):
        return self

parser = CommandParser(completekey=stubborn_str("tab: menu-complete"))

self.completekey+": complete" 现在与self.completekey 相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-04
    • 2011-11-03
    • 2012-06-12
    • 2018-11-18
    • 1970-01-01
    • 2011-06-23
    • 2021-12-02
    • 2013-08-24
    相关资源
    最近更新 更多