Package cliutils :: Module process
[hide private]
[frames] | no frames]

Source Code for Module cliutils.process

  1  __all__ = ['Process', 'sh', 'AlreadyExecuted', 'InvalidCommand'] 
  2   
  3  import os 
  4  import shlex 
  5  from cStringIO import StringIO 
  6  from subprocess import Popen, PIPE 
7 8 -class AlreadyExecuted(Exception):
9 """A command that doesn't exist has been called."""
10 -class InvalidCommand(Exception):
11 """A command that doesn't exist has been called."""
12
13 -def _normalize(cmd):
14 """ 15 Turn the C{cmd} into a list suitable for subprocess. 16 17 @param cmd: The command to be split into a list 18 @type cmd: str, list 19 @return: The split command, or the command passed if no splitting was 20 necessary 21 @rtype: list 22 """ 23 if isinstance(cmd, (str, unicode)): 24 cmd = shlex.split(cmd) 25 return cmd
26
27 -class Process(object):
28 """ 29 A wrapper for subprocess.Popen that allows bash-like pipe syntax and 30 simplified output retrieval. 31 32 Processes will be executed automatically when and if stdout, stderr or a 33 return code are requested. This removes the necessity of calling 34 C{Popen().wait()} manually, or of capturing stdout and stderr from a 35 C{communicate()} call. A small change, to be sure, but it helps reduce 36 overhead for a common pattern. 37 38 One may use the C{|} operator to pipe the output of one L{Process} into 39 another: 40 41 >>> p = Process("echo 'one two three'") | Process("wc -w") 42 >>> print p.stdout 43 3 44 45 """ 46 _stdin = PIPE 47 _stdout = PIPE 48 _stderr = PIPE 49 _retcode = None 50
51 - def __init__(self, cmd, stdin=None):
52 """ 53 @param cmd: A string or list containing the command to be executed. 54 @type cmd: str, list 55 @param stdin: An optional open file object representing input to the 56 process. 57 @type stdin: file 58 @rtype: void 59 """ 60 self._command = _normalize(cmd) 61 if stdin is not None: 62 self._stdin = stdin 63 self._refreshProcess()
64
65 - def __call__(self):
66 """ 67 Shortcut to get process output. 68 69 @return: Process output 70 @rtype: str 71 """ 72 return self.stdout
73
74 - def __or__(self, proc):
75 """ 76 Override default C{or} comparison so that the C{|} operator will work. 77 Don't call this directly. 78 79 @return: Process with C{self}'s stdin as stdout pipe. 80 @rtype: L{Process} 81 """ 82 if self.hasExecuted or proc.hasExecuted: 83 raise AlreadyExecuted("You can't pipe processes after they've been" 84 "executed.") 85 proc._stdin = self._process.stdout 86 proc._refreshProcess() 87 return proc
88 89 @property
90 - def hasExecuted(self):
91 """ 92 A boolean indicating whether or not the process has already run. 93 94 @rtype: bool 95 """ 96 return self._retcode is not None
97
98 - def _refreshProcess(self):
99 if self.hasExecuted: 100 raise AlreadyExecuted("") 101 try: del self._process 102 except AttributeError: pass 103 try: 104 self._process = Popen(self._command, 105 stdin = self._stdin, 106 stdout = self._stdout, 107 stderr = self._stderr) 108 except OSError, e: 109 raise InvalidCommand(" ".join(self._command))
110
111 - def _execute(self):
112 if not self.hasExecuted: 113 self._retcode = self._process.wait()
114
115 - def __str__(self):
116 return self.stdout
117
118 - def __repr__(self):
119 return self.stdout
120 121 @property
122 - def stdout(self):
123 """ 124 Retrieve the contents of stdout, executing the process first if 125 necessary. 126 127 @return: The process output 128 @rtype: str 129 """ 130 self._execute() 131 if not hasattr(self, '_stdoutstorage'): 132 self._stdoutstorage = StringIO(self._process.stdout.read().strip()) 133 return self._stdoutstorage.getvalue()
134 135 @property
136 - def stderr(self):
137 """ 138 Retrieve the contents of stderr, executing the process first if 139 necessary. 140 141 @rtype: str 142 @return: The process error output 143 """ 144 self._execute() 145 if not hasattr(self, '_stderrstorage'): 146 self._stdoutstorage = StringIO(self._process.stderr.read().strip()) 147 return self._stderrstorage.getvalue()
148 149 @property
150 - def retcode(self):
151 """ 152 Get the exit code of the executed process, executing the process 153 first if necessary. 154 155 @rtype: int 156 @return: The exit code of the process 157 """ 158 self._execute() 159 return self._retcode
160 161 @property
162 - def pid(self):
163 """ 164 Get the pid of the executed process. 165 166 @return: The process pid 167 @rtype: int 168 """ 169 self._execute() 170 return self._process.pid
171
172 - def __del__(self):
173 """ 174 Make dead sure the process has been cleaned up when garbage is 175 collected. 176 """ 177 if self.hasExecuted: 178 try: os.kill(self.pid, 9) 179 except: pass
180
181 182 -class _shell(object):
183 """ 184 Singleton class that creates Process objects for commands passed. 185 186 Not meant to be instantiated; use the C{sh} instance. 187 188 >>> p = sh.wc("-w") 189 >>> p.__class__ 190 <class 'cliutils.process.Process'> 191 >>> p._command 192 ['wc', '-w'] 193 194 """
195 - def __getattribute__(self, attr):
196 def inner(cmd=()): 197 command = [attr] 198 command.extend(_normalize(cmd)) 199 return Process(command)
200 return inner
201 sh = _shell() 202