- 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)