Created
June 21, 2011 19:51
-
-
Save dcolish/1038718 to your computer and use it in GitHub Desktop.
Shell like command pipeline in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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