X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/f5a9f89f783c75bccd1ddb68c7b63ad57539d5b6..8cc1078981faa7b5cf00edca711b3df27a14ad0a:/stgit/run.py diff --git a/stgit/run.py b/stgit/run.py index 94dd98e..fa304d0 100644 --- a/stgit/run.py +++ b/stgit/run.py @@ -17,16 +17,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -# popen2 and os.spawn* suck. We should really use subprocess instead, -# but that's only available in Python 2.4 and up, and we try our best -# to stay Python 2.3 compatible. -import popen2, os +import datetime, os, subprocess -import datetime +from stgit.exception import * +from stgit.out import * -from stgit.out import * - -class RunException(Exception): +class RunException(StgException): """Thrown when something bad happened when we tried to run the subprocess.""" pass @@ -44,15 +40,16 @@ class Run: self.__cmd = list(cmd) for c in cmd: if type(c) != str: - raise Exception, 'Bad command: %r' % cmd + raise Exception, 'Bad command: %r' % (cmd,) self.__good_retvals = [0] self.__env = None self.__indata = None - def __log_start(self, cmd): + self.__discard_stderr = False + def __log_start(self): if _log_mode == 'debug': - out.start('Running subprocess %s' % cmd) + out.start('Running subprocess %s' % self.__cmd) elif _log_mode == 'profile': - out.start('Running subprocess %s' % cmd[0]) + out.start('Running subprocess %s' % self.__cmd[0]) self.__starttime = datetime.datetime.now() def __log_end(self, retcode): if _log_mode == 'debug': @@ -60,48 +57,52 @@ class Run: elif _log_mode == 'profile': duration = datetime.datetime.now() - self.__starttime out.done('%1.3f s' % (duration.microseconds/1e6 + duration.seconds)) - def __run_io(self, cmd): - """Run with captured IO. Note: arguments are parsed by the - shell. We single-quote them, so don't use anything with single - quotes in it.""" - if self.__env == None: - ecmd = cmd - else: - ecmd = (['env'] + ['%s=%s' % (key, val) - for key, val in self.__env.iteritems()] - + cmd) - self.__log_start(ecmd) - p = popen2.Popen3(' '.join(["'%s'" % c for c in ecmd]), True) - if self.__indata != None: - p.tochild.write(self.__indata) - p.tochild.close() - outdata = p.fromchild.read() - errdata = p.childerr.read() - self.exitcode = p.wait() >> 8 - self.__log_end(self.exitcode) + def __check_exitcode(self): + if self.__good_retvals == None: + return if self.exitcode not in self.__good_retvals: - raise self.exc('%s failed with code %d:\n%s' - % (cmd[0], self.exitcode, errdata)) - if errdata: - out.warn('call to %s succeeded, but generated a warning:' % cmd[0]) + raise self.exc('%s failed with code %d' + % (self.__cmd[0], self.exitcode)) + def __run_io(self): + """Run with captured IO.""" + self.__log_start() + try: + p = subprocess.Popen(self.__cmd, env = self.__env, + stdin = subprocess.PIPE, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE) + outdata, errdata = p.communicate(self.__indata) + self.exitcode = p.returncode + except OSError, e: + raise self.exc('%s failed: %s' % (self.__cmd[0], e)) + if errdata and not self.__discard_stderr: out.err_raw(errdata) + self.__log_end(self.exitcode) + self.__check_exitcode() return outdata - def __run_noshell(self, cmd): - """Run without captured IO. Note: arguments are not parsed by - the shell.""" - assert self.__env == None + def __run_noio(self): + """Run without captured IO.""" assert self.__indata == None - self.__log_start(cmd) - self.exitcode = os.spawnvp(os.P_WAIT, cmd[0], cmd) + self.__log_start() + try: + p = subprocess.Popen(self.__cmd, env = self.__env) + self.exitcode = p.wait() + except OSError, e: + raise self.exc('%s failed: %s' % (self.__cmd[0], e)) self.__log_end(self.exitcode) - if not self.exitcode in self.__good_retvals: - raise self.exc('%s failed with code %d' - % (cmd[0], self.exitcode)) + self.__check_exitcode() def returns(self, retvals): self.__good_retvals = retvals return self + def discard_exitcode(self): + self.__good_retvals = None + return self + def discard_stderr(self, discard = True): + self.__discard_stderr = discard + return self def env(self, env): - self.__env = env + self.__env = dict(os.environ) + self.__env.update(env) return self def raw_input(self, indata): self.__indata = indata @@ -110,15 +111,15 @@ class Run: self.__indata = ''.join(['%s\n' % line for line in lines]) return self def no_output(self): - outdata = self.__run_io(self.__cmd) + outdata = self.__run_io() if outdata: raise self.exc, '%s produced output' % self.__cmd[0] def discard_output(self): - self.__run_io(self.__cmd) + self.__run_io() def raw_output(self): - return self.__run_io(self.__cmd) + return self.__run_io() def output_lines(self): - outdata = self.__run_io(self.__cmd) + outdata = self.__run_io() if outdata.endswith('\n'): outdata = outdata[:-1] if outdata: @@ -134,11 +135,14 @@ class Run: % (self.__cmd[0], len(outlines))) def run(self): """Just run, with no IO redirection.""" - self.__run_noshell(self.__cmd) + self.__run_noio() def xargs(self, xargs): """Just run, with no IO redirection. The extra arguments are appended to the command line a few at a time; the command is run as many times as needed to consume them all.""" step = 100 + basecmd = self.__cmd for i in xrange(0, len(xargs), step): - self.__run_noshell(self.__cmd + xargs[i:i+step]) + self.__cmd = basecmd + xargs[i:i+step] + self.__run_noio() + self.__cmd = basecmd