def capture_subprocess(cmd, encoding='UTF-8', **popen_kwargs): """Run a command, showing its usual outputs in real time, and return its stdout, stderr output as strings. No temporary files are used. """ stdout = Pty() # libc uses full buffering for stdout if it doesn't see a tty stderr = Pipe() # deadlocks occur if we have any write-end of a pipe open more than once # best practice: close any used write pipes just after spawn outputter = Popen( cmd, stdout=stdout.write, stderr=stderr.write, **popen_kwargs ) stdout.readonly() # deadlock otherwise stderr.readonly() # deadlock otherwise # start one tee each on the original stdout and stderr # writing each to three places: # 1. the original destination # 2. a pipe just for that one stream stdout_tee = Tee(stdout.read, STDOUT) stderr_tee = Tee(stderr.read, STDERR) # clean up left-over processes and pipes: exit_code = outputter.wait() result = (stdout_tee.join(), stderr_tee.join()) if encoding is not None: result = tuple( bytestring.decode(encoding) for bytestring in result ) if exit_code == 0: return result else: error = CalledProcessError(exit_code, cmd) error.result = result raise error
def capture_subprocess(cmd, encoding='UTF-8', **popen_kwargs): """Run a command, showing its usual outputs in real time, and return its stdout, stderr output as strings. No temporary files are used. """ stdout = Pty( ) # libc uses full buffering for stdout if it doesn't see a tty stderr = Pipe() # deadlocks occur if we have any write-end of a pipe open more than once # best practice: close any used write pipes just after spawn outputter = Popen(cmd, stdout=stdout.write, stderr=stderr.write, **popen_kwargs) stdout.readonly() # deadlock otherwise stderr.readonly() # deadlock otherwise # start one tee each on the original stdout and stderr # writing each to three places: # 1. the original destination # 2. a pipe just for that one stream stdout_tee = Tee(stdout.read, STDOUT) stderr_tee = Tee(stderr.read, STDERR) # clean up left-over processes and pipes: exit_code = outputter.wait() result = (stdout_tee.join(), stderr_tee.join()) if encoding is not None: result = tuple(bytestring.decode(encoding) for bytestring in result) if exit_code == 0: return result else: error = CalledProcessError(exit_code, cmd) error.result = result raise error