繁体   English   中英

更换sys.stdout时,cmd.Cmd中的Python readline选项卡完成

[英]Python readline tab-completion in cmd.Cmd, when sys.stdout has been replaced

目前,我有一个应用程序,它使用cmd.Cmd模块作为命令行界面,tab-completion完美地工作。

现在,我想用另一个对象替换sys.stdout (例如,为了捕获正在编写的内容。)

在理论上,以下代码段应该是完全透明的,因为对Std对象的每个get / set操作都被重定向到actial sys.__stdout__

class Std(object):
    def __getattribute__(self, name):
        if name in ('__getattribute__', '__setattr__'):
            return object.__getattribute__(self, name)
        else:
            return getattr(sys.__stdout__, name)

    def __setattr__(self, name, value):
        setattr(sys.__stdout__, name, value)

sys.stdout = Std()

例如, sys.stdout.fileno()仍将打印1.但是, Cmd.cmd的readline选项卡完成现在不再有效...

好的,让我们继承文件。 (stdout是一个文件对象。)

class Std(file):
    def __init__(self):
        pass
    def __getattribute__(self, name):
        if name in ('__getattribute__', '__setattr__'):
            return object.__getattribute__(self, name)
        else:
            return getattr(sys.__stdout__, name)

    def __setattr__(self, name, value):
        setattr(sys.__stdout__, name, value)

sys.stdout = Std()

现在我得到:

Traceback (most recent call last):
  File "./shell.py", line 61, in <module>
    print '#1'
ValueError: I/O operation on closed file

但是,以下断言不会失败:

assert not sys.stdout.closed

不知怎的,我猜,Python过度优化了某些东西,绕过了Std.write。

如何在不失去readline支持的情况下更换stdout应该怎么办?

乔纳森

-编辑-

我也试图替换sys.stdin。 将其传递给cmd.Cmd不起作用,因为raw_input用于readline支持,而Python的raw_input不接受文件描述符。 它回退到分配给sys.stdin的pty。

当我创建一个新的pty(通过os.openpty()),并将该对赋值给sys.stdin / out时,通过该pty的readline自动完成工作完美,但同样,当包装在代理对象中时,它可以工作,但没有自动完成。

试图理解readline的来源,但这并不容易: http//svn.python.org/projects/python/branches/release25-maint/Modules/readline.c

sys.stdout地知道为什么替换sys.stdout不起作用,但无论如何你可以通过将自己的文件对象传递给cmd.Cmd的构造函数来解决你的问题。

这是一个示例脚本(部分借用PyMOTW ),演示了tab-completion:

import sys, cmd

class Std(object):
    def __getattribute__(self, name):
        if name in ('__getattribute__', '__setattr__'):
            return object.__getattribute__(self, name)
        else:
            return getattr(sys.__stdout__, name)

    def __setattr__(self, name, value):
        setattr(sys.__stdout__, name, value)

class HelloWorld(cmd.Cmd):
    FRIENDS = [ 'Alice', 'Adam', 'Barbara', 'Bob' ]

    def do_greet(self, person):
        "Greet the person"
        if person and person in self.FRIENDS:
            greeting = 'hi, %s!' % person
        elif person:
            greeting = "hello, " + person
        else:
            greeting = 'hello'
        print greeting

    def complete_greet(self, text, line, begidx, endidx):
        if not text:
            completions = self.FRIENDS[:]
        else:
            completions = [f for f in self.FRIENDS
                           if f.startswith(text)]
        return completions

    def do_EOF(self, line):
        return True

if __name__ == '__main__':

    HelloWorld(stdout=Std()).cmdloop()

出于您的目的,这是一种更好的处理方式,因为它确保您只捕获Cmd实例生成的输出。

请注意,您传入的文件对象也可用作Cmd实例上的stdout属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM