Наличие консоли в однопоточном скрипте Python - PullRequest
3 голосов
/ 22 ноября 2010

Я хотел бы иметь интерактивную консоль в однопоточном скрипте, в которой открыто несколько соединений TCP. Это означает, что у меня не может быть стандартного ввода, блокирующего поток.

Есть ли простой способ сделать это? Или я должен просто поместить консоль в отдельный поток и покончить с этим?

Ответы [ 2 ]

3 голосов
/ 22 ноября 2010

Вы можете создать подкласс InteractiveConsole (из встроенного модуля 'code') и переопределить метод push () с помощью оболочки, которая перенаправляет stdout / stderr на экземпляр StringIO перед отправкой в ​​базовый метод push () InteractiveConsole.Ваша оболочка может вернуть 2-кортеж (больше, результат), где «больше» указывает, ожидает ли InteractiveConsole больше ввода, а «результат» - это то, что InteractiveConsole.push () записало в ваш экземпляр StringIO.

Звучит сложнеечем это.Вот основная предпосылка:

import sys
from cStringIO import StringIO
from code import InteractiveConsole
from contextlib import contextmanager

__all__ = ['Interpreter']


@contextmanager
def std_redirector(stdin=sys.stdin, stdout=sys.stdin, stderr=sys.stderr):
    """Temporarily redirect stdin/stdout/stderr"""

    tmp_fds = stdin, stdout, stderr
    orig_fds = sys.stdin, sys.stdout, sys.stderr
    sys.stdin, sys.stdout, sys.stderr = tmp_fds
    yield
    sys.stdin, sys.stdout, sys.stderr = orig_fds


class Interpreter(InteractiveConsole):
    """Remote-friendly InteractiveConsole subclass

    This class behaves just like InteractiveConsole, except that it
    returns all output as a string rather than emitting to stdout/stderr

    """
    banner = ("Python %s\n%s\n" % (sys.version, sys.platform) +
              'Type "help", "copyright", "credits" or "license" '
              'for more information.\n')

    ps1 = getattr(sys, "ps1", ">>> ")
    ps2 = getattr(sys, "ps2", "... ")


    def __init__(self, locals=None):
        InteractiveConsole.__init__(self, locals=locals)
        self.output = StringIO()
        self.output = StringIO()

    def push(self, command):
        """Return the result of executing `command`

        This function temporarily redirects stdout/stderr and then simply
        forwards to the base class's push() method.  It returns a 2-tuple
        (more, result) where `more` is a boolean indicating whether the
        interpreter expects more input [similar to the base class push()], and
        `result` is the captured output (if any) from running `command`.

        """
        self.output.reset()
        self.output.truncate()
        with std_redirector(stdout=self.output, stderr=self.output):
            try:
                more = InteractiveConsole.push(self, command)
                result = self.output.getvalue()
            except (SyntaxError, OverflowError):
                pass
            return more, result

Посмотрите на этот полный пример, который принимает ввод от сокета UDP:

Startдве консоли и запустите server.py в одной, client.py в другой.То, что вы видите в client.py, должно быть неотличимо от обычного интерактивного интерпретатора python, даже если все команды возвращаются в server.py для оценки.

Конечно, использование таких сокетов ужасно небезопасно, ноон показывает, как асинхронно оценивать внешний вход.Вы должны быть в состоянии адаптировать его к вашей ситуации, если вы доверяете источнику ввода.Вещи становятся «интересными», когда кто-то печатает:

while True: continue

Но это совсем другая проблема ...: -)

0 голосов
/ 22 ноября 2010

Подойдет либо однопоточный, либо многопоточный, но если вы решите не использовать потоки, вам нужно будет использовать опрос (в C это может быть сделано, например, с помощью poll (2)) и проверить, Консоль и / или TCP-соединения имеют готовый ввод.

...