You can subclass InteractiveConsole (from the built-in "code" module) and override the push () method with a wrapper that redirects stdout / stderr to a StringIO instance before sending it to the InteractiveConsole push () method. Your wrapper can return a 2-tuple (more, result), where "more" indicates whether InteractiveConsole expects more input and "result" is what InteractiveConsole.push () wrote your StringIO instance.
Sounds harder than it is. Here's the basic assumption:
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 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 that accepts input from a UDP socket:
Launch 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, although all commands are rounded to the .py server for evaluation.
Of course, using such sockets is terribly unsafe, but this illustrates how to evaluate external input asynchronously. You must be able to adapt it to your situation if you trust the input source. Things become βinterestingβ when someone types:
while True: continue
But this other problem is completely ... :-)