当 sys.stdout 被替换时,cmd.Cmd 中的 Python readline tab 补全

发布于 2024-12-25 14:39:12 字数 1955 浏览 4 评论 0原文

目前,我有一个使用 cmd.Cmd 模块作为命令行界面的应用程序,制表符补全功能完美运行。

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

理论上,以下代码片段应该是完全透明的,因为每个 get/set 操作Std 对象被重定向到实际的 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 tab-completion 现在不再起作用...

好吧,让我们从 file 继承。 (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 支持......?

乔纳森

-编辑-

我也在尝试替换 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

Currently, I have an application which uses the cmd.Cmd module for the command line interface, tab-completion works perfectly.

Now, I'd like to replace sys.stdout, with another object (For example, in order to capture what's being written.)

Following snippet should be completely transparent in theory, as every get/set operation to the Std object is redirected to the 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()

For example, sys.stdout.fileno() will still print 1.
However, the readline tab-completion of Cmd.cmd does no longer work now...

Okay, let's inherit from file. (stdout is a file object.)

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()

And now I get:

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

However, the following assertion does not fail:

assert not sys.stdout.closed

Somehow, I guess, Python is overoptimizing something, and bypassing Std.write.

What should I do to replace stdout, without loosing readline support...?

Jonathan

-edit-

I'm also trying to replace sys.stdin.
Passing it to cmd.Cmd does not work, because raw_input is used for readline support and Python's raw_input does not accept a file descriptor. It falls back to whatever pty is assigned to sys.stdin.

When I create a new pty (through os.openpty()), and assign this pair to sys.stdin/out, readline autocompletion through that pty works perfectly, but again, when wrapped in a proxy object, it works, but without autocompletion.

Trying to understand the source of readline, but it isn't easy:
http://svn.python.org/projects/python/branches/release25-maint/Modules/readline.c

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

靑春怀旧 2025-01-01 14:39:12

我不确切知道为什么替换 sys.stdout 不起作用,但无论如何,您可以通过将自己的文件对象传递给 cmd.Cmd

这是一个示例脚本(部分借自 PyMOTW),演示制表符补全:

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 属性。

I don't know precisely why replacing sys.stdout does not work, but in any case you can fix your immediate problems by passing your own file object to the constructor for cmd.Cmd.

Here is an example script (partly borrowed from PyMOTW), that demonstrates 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()

For your purposes, this is a much better way of doing things, because it ensures you will only be capturing the output that your Cmd instance produces.

Note that the file object that you pass in is also available as a stdout attribute on the Cmd instance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文