PYIO_HAVE_BLOCKING = True import os import sys from os import O_NONBLOCK from fcntl import fcntl, F_GETFL, F_SETFL from time import sleep from subprocess import Popen from select import select if PYIO_HAVE_BLOCKING: import _pyio import platform from os import kill from signal import SIGKILL from time import time def _file_setblocking(fd, blocking): flags = fcntl(fd, F_GETFL) if blocking: flags |= O_NONBLOCK else: flags &= ~O_NONBLOCK fcntl(fd, F_SETFL, flags) def communicateProcess(process, timeout): if platform.system() == 'Windows': raise NotImplemented pid = process.pid start = time() pause = 0.025 # Setup pipes stdout = None stderr = None chunk_size = 4096 read_pipes = True pipes_fd = [] if process.stdout is not None: stdout_fd = process.stdout.fileno() if PYIO_HAVE_BLOCKING: # io module has no setblocking() methods yet, use _pyio module instead process.__stdout__ = process.stdout process.stdout = _pyio.BufferedReader(process.stdout.raw) process.stdout.setblocking(False) else: _file_setblocking(stdout_fd, False) pipes_fd.append(stdout_fd) stdout = [] else: stdout_fd = None if process.stderr is not None: stderr_fd = process.stderr.fileno() if PYIO_HAVE_BLOCKING: # io module has no setblocking() methods yet, use _pyio module instead process.__stderr__ = process.stderr process.stderr = _pyio.BufferedReader(process.stderr.raw) process.stderr.setblocking(False) else: _file_setblocking(stderr_fd, False) pipes_fd.append(stderr_fd) stderr = [] else: stderr_fd = None if pipes_fd: charset = 'utf-8' def decodeOutput(chunks): return [ str(line, charset, "replace") for line in b''.join(chunks).splitlines()] else: read_pipes = False while True: # Execution timeout? since = time() - start if timeout < since: break # Read pipes if read_pipes: while 1: read, write, err = select(pipes_fd, (), (), 0) if not read: break done = False if stdout_fd in read: print("read stdout (%s)" % (chunk_size,)) if PYIO_HAVE_BLOCKING: chunk = process.stdout.read(chunk_size) else: chunk = os.read(stdout_fd, chunk_size) print("read stdout (%s) -> %s" % (chunk_size, len(chunk))) stdout.append(chunk) if not chunk: done = True if stderr_fd in read: print("read stderr (%s)" % (chunk_size, )) if PYIO_HAVE_BLOCKING: chunk = process.stderr.read(chunk_size) else: chunk = os.read(stderr_fd, chunk_size) print("read stderr (%s) -> %s" % (chunk_size, len(chunk))) stderr.append(chunk) if not chunk: done = True if done: break # Read process status status = process.poll() if status is not None: # Process exit: done if stdout is not None: stdout = decodeOutput(stdout) if stderr is not None: stderr = decodeOutput(stderr) return status, stdout, stderr # Sleep few milliseconds if 5.0 <= since: pause = 0.500 elif 1.0 <= since: pause = 0.250 elif 0.250 <= since: pause = 0.100 sleep(pause) # Terminate the process kill(process.pid, SIGKILL) # Raise an error (execution timeout) raise Exception("Abort process %s: timeout (%.1f sec)!" % (pid, since)) def main(): from subprocess import PIPE from pprint import pprint process = Popen([sys.executable], stdout=PIPE, stderr=PIPE) status, stdout, stderr = communicateProcess(process, 10.0) print("STDOUT") pprint(stdout) print print("STDERR") pprint(stderr) main()