在单线程 Python 脚本中拥有控制台

发布于 2024-10-04 08:54:59 字数 107 浏览 0 评论 0原文

我希望在单线程脚本中有一个交互式控制台,并打开多个 TCP 连接。这意味着我不能仅仅让标准输入阻塞线程。

有没有简单的方法可以做到这一点?或者我应该将控制台放在它自己的线程中并完成它?

I would like to have an interactive console in a single-threaded script that has several TCP connections open. This means I can't just have a standard input blocking the thread.

Is there an easy way to do this? Or should I just put the console in its own thread and be done with it?

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

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

发布评论

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

评论(2

执着的年纪 2024-10-11 08:54:59

您可以将 InteractiveConsole 子类化(来自内置“代码”模块)并
使用重定向 stdout/stderr 的包装器覆盖 push() 方法
在转发到基础之前到 StringIO 实例
InteractiveConsole 的push() 方法。你的包装器可以返回一个 2 元组
(more, result) 其中“more”表示 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 套接字的输入:

启动两个控制台并在其中一个控制台中运行 server.py,在另一个控制台中运行 client.py。
您在 client.py 中看到的内容应该与 python 中的没有区别
常规交互式解释器,即使所有命令都在
往返到 server.py 进行评估。

当然,像这样使用套接字是非常不安全的,但它
说明如何异步评估外部输入。你
应该能够根据您的情况进行调整,只要您相信
输入源。当有人打字时,事情变得“有趣”:

while True: continue

但这完全是另一个问题......:-)

You can subclass InteractiveConsole (from the builtin 'code' module) and
override the push() method with a wrapper that redirects stdout/stderr
to a StringIO instance before forwarding to the base
InteractiveConsole's push() method. Your wrapper can return a 2-tuple
(more, result) where 'more' indicates whether InteractiveConsole expects
more input, and 'result' is whatever InteractiveConsole.push() wrote to
your StringIO instance.

It sounds harder than it is. Here's the basic premise:

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

Check out this complete example, which accepts input from a UDP socket:

Start two consoles and run server.py in one, client.py in the other.
What you see in client.py should be indistinguishable from python's
regular interactive interpreter, even though all commands are being
round-tripped to server.py for evaluation.

Of course, using sockets like this is terribly insecure, but it
illustrates how to evaluate an external input asynchronously. You
should be able to adapt it to your situation, as long as you trust the
input source. Things get 'interesting' when someone types:

while True: continue

But that's another problem entirely... :-)

孤檠 2024-10-11 08:54:59

单线程或多线程都可以,但如果您选择不使用线程,则需要使用轮询(例如,在 C 中可以使用 poll(2) 来完成)并检查控制台和/或者 TCP 连接已准备好输入。

Either single-threaded or multi-threaded will do, but if you choose not to use threads, you will need to use polling (in C this may be done using poll(2), for example) and check for whether the console and/or the TCP connections have input ready.

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