Skip to content

Instantly share code, notes, and snippets.

@bchess
Created November 12, 2019 20:37
Show Gist options
  • Save bchess/b9cfd0ed1a2297b9165995ab7263b3bc to your computer and use it in GitHub Desktop.
Save bchess/b9cfd0ed1a2297b9165995ab7263b3bc to your computer and use it in GitHub Desktop.
subprocess.check_output while tee-ing output to stdout/stderr
def check_output_splitlines(args: List[str]) -> List[str]:
"""This is like subprocess.check_output().split('\n'), however it also
copies all stderr and stdout to our own sys.stderr/stdout
"""
stdout_buffer = io.StringIO(newline=None)
# use pty instead of pipes so python subprocesses do not buffer
stdout_ours, stdout_theirs = pty.openpty()
stderr_ours, stderr_theirs = pty.openpty()
proc = subprocess.Popen(args, bufsize=0, stdout=stdout_theirs, stderr=stderr_theirs)
selector = selectors.DefaultSelector()
selector.register(stdout_ours, selectors.EVENT_READ, (stdout_ours, sys.stdout))
selector.register(stderr_ours, selectors.EVENT_READ, (stderr_ours, sys.stderr))
os.close(stdout_theirs)
os.close(stderr_theirs)
registered = 2
while registered:
events = selector.select()
for key, mask in events:
our_pipe, stdio_file = key.data
try:
data = os.read(our_pipe, 8192).decode()
except OSError as e:
if e.errno != errno.EIO:
raise e
data = ''
if not data:
selector.unregister(our_pipe)
registered -= 1
continue
if stdio_file is sys.stdout:
stdout_buffer.write(data)
stdio_file.write(data)
stdio_file.flush()
proc.wait()
if proc.returncode != 0:
raise subprocess.CalledProcessError(proc.returncode, args, '', '')
return stdout_buffer.getvalue().split('\n')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment