Having a console in a single threaded Python script

I would like to have an interactive console in a single-threaded script that opens several TCP connections. This means that I cannot just have a standard input blocking the stream.

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

+3
python multithreading interactive console
source share
2 answers

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 ... :-)

+3
source share

It will run single-threaded or multi-threaded, but if you decide not to use threads, you will need to use polling (in C this can be done, for example, by polling (2)) and check that the console and / or TCP connections have a ready input.

0
source share

All Articles