importsysimportsubprocessimportrandomimporttimeimportthreadingimportQueueclassAsynchronousFileReader(threading.Thread):'''
Helper class to implement asynchronous reading of a file
in a separate thread. Pushes read lines on a queue to
be consumed in another thread.
'''def__init__(self,fd,queue):assertisinstance(queue,Queue.Queue)assertcallable(fd.readline)threading.Thread.__init__(self)self._fd=fd
self._queue=queuedefrun(self):'''The body of the tread: read lines and put them on the queue.'''forlineiniter(self._fd.readline,''):self._queue.put(line)defeof(self):'''Check whether there is no more content to expect.'''returnnotself.is_alive()andself._queue.empty()defconsume(command):'''
Example of how to consume standard output and standard error of
a subprocess asynchronously without risk on deadlocking.
'''# Launch the command as subprocess.process=subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.PIPE)# Launch the asynchronous readers of the process' stdout and stderr.stdout_queue=Queue.Queue()stdout_reader=AsynchronousFileReader(process.stdout,stdout_queue)stdout_reader.start()stderr_queue=Queue.Queue()stderr_reader=AsynchronousFileReader(process.stderr,stderr_queue)stderr_reader.start()# Check the queues if we received some output (until there is nothing more to get).whilenotstdout_reader.eof()ornotstderr_reader.eof():# Show what we received from standard output.whilenotstdout_queue.empty():line=stdout_queue.get()print'Received line on standard output: '+repr(line)# Show what we received from standard error.whilenotstderr_queue.empty():line=stderr_queue.get()print'Received line on standard error: '+repr(line)# Sleep a bit before asking the readers again.time.sleep(.1)# Let's be tidy and join the threads we've started.stdout_reader.join()stderr_reader.join()# Close subprocess' file descriptors.process.stdout.close()process.stderr.close()defproduce(items=10):'''
Dummy function to randomly render a couple of lines
on standard output and standard error.
'''foriinrange(items):output=random.choice([sys.stdout,sys.stderr])output.write('Line %d on %s\n'%(i,output))output.flush()time.sleep(random.uniform(.1,1))if__name__=='__main__':# The main flow:# if there is an command line argument 'produce', act as a producer# otherwise be a consumer (which launches a producer as subprocess).iflen(sys.argv)==2andsys.argv[1]=='produce':produce(10)else:consume(['python',sys.argv[0],'produce'])