Skip to content

Instantly share code, notes, and snippets.

@iainlane
Created February 24, 2017 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iainlane/8b96022e3b98901b25cc5bcab6b43e02 to your computer and use it in GitHub Desktop.
Save iainlane/8b96022e3b98901b25cc5bcab6b43e02 to your computer and use it in GitHub Desktop.
intercept_stderr
#!/usr/bin/python3
from splice import tee, splice
import asyncio.subprocess, errno, io, os, sys
# Run argv, return its stderr while also keeping it output to sys.stderr
def intercept_stderr(argv):
@asyncio.coroutine
def call_it(loop):
(r, w) = os.pipe()
# Start looking at the read end of our pipe
f = loop.run_in_executor(None, peek_pipe, r, sys.stderr)
# Fire off the process, setting stderr to the write end
create = asyncio.create_subprocess_exec(argv, stderr=w)
proc = yield from create
yield from proc.wait()
# It's finished - this will make tee() inside peek_pipe() return EINVAL
# so that it returns
os.close(r)
os.close(w)
res = yield from f
return res
# Read from 'r', putting results in 'dest' and returning the data read
def peek_pipe(r, dest):
ret = io.StringIO()
while True:
(r2, w2) = os.pipe()
try:
# Copy from r to w2, without consuming from r
l = tee (r, w2, sys.maxsize, 0)
# Now consume r by putting it into dest (the place where it
# came from, i.e. stderr)
splice(r, None, dest, None, l, 0)
# And also consume r2
with open(r2, 'r') as f:
ret.write(f.read(l))
os.close(w2)
except OSError as e:
# This happens when call_it() closes our input pipe
if e.errno == errno.EINVAL:
return ret.getvalue()
raise
loop = asyncio.get_event_loop()
r = loop.run_until_complete(call_it(loop))
loop.close()
return r
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment