I'd like to have a Python Cmd instance running in a separate thread, and be able to write input and read output from other parts of my program. In the constructor of Cmd, it is possible to specify stdin(default: sys.stdin) and stdout(default sys.stdout). I was expecting I could use the StringIO module for reading and writing, but the Cmd just reads the initial string, reaches EOF and exits. I need the same behaviour as with sys.stdin, thus that readline() will block until there is input to read.
I made this class, which works as I want:
import Queue
class BlockingLineIO():
def __init__(self):
self.closed = False
self.queue = Queue.Queue()
self.remainder = ""
def write(self, s):
for line in s.splitlines(True):
if not line.endswith("\n"):
self.remainder = line
elif line != "":
self.queue.put(self.remainder + line)
self.remainder = ""
def read(self):
if self.queue.empty() and self.closed:
return ""
else:
self.queue.put(False)
return "".join(list(iter(self.queue.get, False)))
def readline(self):
if self.queue.empty() and self.closed:
return ""
else:
return self.queue.get()
def flush(self):
pass
def close(self):
self.queue.put("")
self.closed = True
def main():
import cmd, threading
my_cmd_class = type("Foo", (cmd.Cmd, object),
{'do_EOF': lambda self, line: self.stdout.write("Bye.\n") == None})
my_cmd = my_cmd_class(stdin=BlockingLineIO(), stdout=BlockingLineIO())
my_cmd.use_rawinput = False
my_cmd.prompt = ""
cmd_loop = threading.Thread(target=my_cmd.cmdloop)
cmd_loop.start()
my_cmd.stdin.write("help\n")
print my_cmd.stdout.readline()
print my_cmd.stdout.read()
my_cmd.stdin.close()
cmd_loop.join()
print my_cmd.stdout.read()
if __name__ == '__main__':
main()
The question:
Is the above a normal way of achieving what I need? Is there any standard class which I should use for piping stdin and stdout? I get the feeling that I am missing something.