Skip to content

Instantly share code, notes, and snippets.

@dcolish
Created June 21, 2011 19:51
Show Gist options
  • Select an option

  • Save dcolish/1038718 to your computer and use it in GitHub Desktop.

Select an option

Save dcolish/1038718 to your computer and use it in GitHub Desktop.
Shell like command pipeline in python
from collections import namedtuple
from os import unlink
from os.path import join as pjoin
from subprocess import PIPE, Popen
class Path(object):
def __init__(self, pathname):
self.pathname = pathname
def __repr__(self):
return self.pathname
def __div__(self, b):
if isinstance(b, Path):
self.pathname = pjoin(self.pathname, b.pathname)
return self
elif isinstance(b, str):
self.pathname = pjoin(self.pathname, b)
else:
raise ValueError("Cannot join object into path")
return self
def unlink(self):
unlink(self.pathname)
class ShellOutput(namedtuple("ShellOutput", "retcode stdout stderr")):
def __gt__(self, b):
"""
:param b: will object to redirect the stdout into
"""
self.redirect(b, 'stdout')
def redirect(self, path_out, fd):
if isinstance(path_out, Path):
path = path_out.pathname
elif isinstance(path_out, str):
path = path_out
else:
raise ValueError("Cannot redirect to this object")
with open(path, 'w') as out:
print >> out, getattr(self, fd)
class ShellCmd(object):
"""
This is a wrapper on Popen to help make building pipelines of commands
easier. The convenience syntax of '|' is reminiscent of regular shell
semantics but it is a bit different. In this case you must explicitly call
the resulting object
"""
def __init__(self, *cmd):
self.cmds = [cmd]
self.result = None
def __call__(self, stdin=''):
return self.run(stdin)
def __gt__(self, b):
if isinstance(b, str) or isinstance(b, Path):
self() > b
def __or__(self, b):
return self.pipe(b)
def __repr__(self):
return self().stdout
def pipe(self, b):
if isinstance(b, ShellCmd):
b.cmds = self.cmds + b.cmds
return b
def run(self, stdin=()):
first = True
procs = []
if isinstance(stdin, ShellCmd):
self.cmds = stdin.cmds + self.cmds
stdin = ''
for cmd in self.cmds:
if first:
if stdin != '':
proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
else:
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
first = False
else:
proc = Popen(cmd, stdin=procs[-1].stdout,
stdout=PIPE, stderr=PIPE)
procs.append(proc)
if len(procs) > 1:
if stdin != '':
procs[0].communicate(stdin)
for proc in procs[:-1]:
proc.stdout.close()
stdout, stderr = procs[-1].communicate()
retcode = procs[-1].returncode
else:
stdout, stderr = procs[0].communicate()
retcode = procs[0].returncode
return ShellOutput(retcode, stdout, stderr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment