Python readline tab-complete в cmd.Cmd, когда sys.stdout был заменен - PullRequest
5 голосов
/ 07 января 2012

В настоящее время у меня есть приложение, которое использует модуль cmd.Cmd для интерфейса командной строки, завершение табуляции работает отлично.

Теперь я хотел бы заменить sys.stdout другим объектом (например, для захвата того, что пишется).

Следующий фрагмент должен быть полностью прозрачным в теории, так как каждая операция get / set для объекта Std перенаправляется на 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 больше не работает ...

Хорошо, давайте наследуем из файла. (стандартный вывод является файловым объектом.)

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 ...?

Jonathan

-edit-

Я также пытаюсь заменить sys.stdin. Передача его в cmd.Cmd не работает, потому что raw_input используется для поддержки readline, а Python raw_input не принимает файловый дескриптор. Он возвращается к любому pty, назначенному для sys.stdin.

Когда я создаю новый pty (через os.openpty ()) и назначаю эту пару для sys.stdin / out, автодополнение readline через этот pty работает отлично, но опять же, когда он помещается в прокси-объект, он работает, но без автозаполнения.

Попытка понять источник readline, но это нелегко: http://svn.python.org/projects/python/branches/release25-maint/Modules/readline.c

1 Ответ

1 голос
/ 08 января 2012

Я не знаю точно, почему замена 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.

Обратите внимание, что передаваемый вами файловый объект также доступен как атрибут stdout в экземпляре Cmd.

...