Read Subprocess Stdout And Stderr Concurrently
Solution 1:
I did that once.. here is some old code I wrote
classProcess_Communicator():
defjoin(self):
self.te.join()
self.to.join()
self.running = False
self.aggregator.join()
self.ti.join()
defenqueue_in(self):
while self.running and self.p.stdin isnotNone:
whilenot self.stdin_queue.empty():
s = self.stdin_queue.get()
self.p.stdin.write(str(s) + '\n\r')
passdefenqueue_output(self):
ifnot self.p.stdout or self.p.stdout.closed:
return
out = self.p.stdout
for line initer(out.readline, b''):
self.qo.put(line)
# out.flush()defenqueue_err(self):
ifnot self.p.stderr or self.p.stderr.closed:
return
err = self.p.stderr
for line initer(err.readline, b''):
self.qe.put(line)
defaggregate(self):
while (self.running):
self.update()
self.update()
defupdate(self):
line = ""try:
while self.qe.not_empty:
line = self.qe.get_nowait() # or q.get(timeout=.1)
self.unbblocked_err += line
except Queue.Empty:
pass
line = ""try:
while self.qo.not_empty:
line = self.qo.get_nowait() # or q.get(timeout=.1)
self.unbblocked_out += line
except Queue.Empty:
passwhilenot self.stdin_queue.empty():
s = self.stdin_queue.get()
self.p.stdin.write(str(s))
defget_stdout(self, clear=True):
ret = self.unbblocked_out
if clear:
self.unbblocked_out = ""return ret
defhas_stdout(self):
ret = self.get_stdout(False)
if ret == '':
returnNoneelse:
return ret
defget_stderr(self, clear=True):
ret = self.unbblocked_out
if clear:
self.unbblocked_out = ""return ret
defhas_stderr(self):
ret = self.get_stdout(False)
if ret == '':
returnNoneelse:
return ret
def__init__(self, subp):
'''This is a simple class that collects and aggregates the
output from a subprocess so that you can more reliably use
the class without having to block for subprocess.communicate.'''
self.p = subp
self.unbblocked_out = ""
self.unbblocked_err = ""
self.running = True
self.qo = Queue.Queue()
self.to = threading.Thread(name="out_read",
target=self.enqueue_output,
args=())
self.to.daemon = True# thread dies with the program
self.to.start()
self.qe = Queue.Queue()
self.te = threading.Thread(name="err_read",
target=self.enqueue_err,
args=())
self.te.daemon = True# thread dies with the program
self.te.start()
self.stdin_queue = Queue.Queue()
self.aggregator = threading.Thread(name="aggregate",
target=self.aggregate,
args=())
self.aggregator.daemon = True# thread dies with the program
self.aggregator.start()
passYou may not need the whole example, but feel free to cut copy and paste what you need. It's also important to show how I did the threading.
Solution 2:
The code looks more complicated than the task requires. I don't see why do you need to call process.poll() or queue.get_nowait() here. To deliver subprocess' stdout/stderr to several sinks; you could start with teed_call() that accepts arbitrary file-like objects: you could pass logfiles and special file-like objects that accumulates errors in theirs .write() methods.
To fix your code with minimal changes; you should call .join() on the reader threads (even if process.poll() is notNone i.e., the subprocess exited; there could be some pending output. Joining reader's threads ensures that all output is read).
Post a Comment for "Read Subprocess Stdout And Stderr Concurrently"