Skip to content

Instantly share code, notes, and snippets.

@diefans
Created March 10, 2016 18:19
Show Gist options
  • Select an option

  • Save diefans/b8bc601ca34ed8571771 to your computer and use it in GitHub Desktop.

Select an option

Save diefans/b8bc601ca34ed8571771 to your computer and use it in GitHub Desktop.
Popen stdout/stderr generator by means of select.select
import shlex
import subprocess
import select
class stdout(str):
"""A string representation from stdout."""
class stderr(str):
"""A string representation from stderr."""
class Execution(object):
"""Provides an iterator over stdout and stderr lines from Popen process."""
def __init__(self, command):
if isinstance(command, subprocess.Popen):
self.proc = command
else:
self.proc = subprocess.Popen(
isinstance(command, basestring) and shlex.split(command) or command,
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
# stdin=subprocess.PIPE,
)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __iter__(self):
proc = self.proc
stdout_fd = proc.stdout.fileno()
stderr_fd = proc.stderr.fileno()
line_factory = {
stdout_fd: lambda: stdout(proc.stdout.readline()),
stderr_fd: lambda: stderr(proc.stderr.readline())
}
reads = [stdout_fd, stderr_fd]
while True:
rx, _, _ = select.select(reads, [], [])
for fd in rx:
factory = line_factory[fd]
while True:
line = factory()
if line == '':
break
yield line
if proc.poll() is not None:
break
@property
def exitcode(self):
return self.proc.returncode
@property
def pid(self):
return self.proc.pid
def test_execution():
with Execution('ls -la') as exe:
for line in exe:
print line.__class__.__name__, line,
print "Exited with:", exe.exitcode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment