diff options
Diffstat (limited to 'cvs2svn_lib/process.py')
-rw-r--r-- | cvs2svn_lib/process.py | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/cvs2svn_lib/process.py b/cvs2svn_lib/process.py new file mode 100644 index 0000000..56469ce --- /dev/null +++ b/cvs2svn_lib/process.py @@ -0,0 +1,116 @@ +# (Be in -*- python -*- mode.) +# +# ==================================================================== +# Copyright (c) 2000-2008 CollabNet. All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://subversion.tigris.org/license-1.html. +# If newer versions of this license are posted there, you may use a +# newer version instead, at your option. +# +# This software consists of voluntary contributions made by many +# individuals. For exact contribution history, see the revision +# history and logs, available at http://cvs2svn.tigris.org/. +# ==================================================================== + +"""This module contains generic utilities used by cvs2svn.""" + + +import subprocess + +from cvs2svn_lib.common import FatalError +from cvs2svn_lib.common import CommandError + + +def call_command(command, **kw): + """Call the specified command, checking that it exits successfully. + + Raise a FatalError if the command cannot be executed, or if it exits + with a non-zero exit code. Pass KW as keyword arguments to + subprocess.call().""" + + try: + retcode = subprocess.call(command, **kw) + if retcode < 0: + raise FatalError( + 'Command terminated by signal %d: "%s"' + % (-retcode, ' '.join(command),) + ) + elif retcode > 0: + raise FatalError( + 'Command failed with return code %d: "%s"' + % (retcode, ' '.join(command),) + ) + except OSError, e: + raise FatalError( + 'Command execution failed (%s): "%s"' + % (e, ' '.join(command),) + ) + + +class CommandFailedException(Exception): + """Exception raised if check_command_runs() fails.""" + + pass + + +def check_command_runs(cmd, cmdname): + """Check whether the command CMD can be executed without errors. + + CMD is a list or string, as accepted by subprocess.Popen(). CMDNAME + is the name of the command as it should be included in exception + error messages. + + This function checks three things: (1) the command can be run + without throwing an OSError; (2) it exits with status=0; (3) it + doesn't output anything to stderr. If any of these conditions is + not met, raise a CommandFailedException describing the problem.""" + + try: + pipe = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except OSError, e: + raise CommandFailedException('error executing %s: %s' % (cmdname, e,)) + pipe.stdin.close() + pipe.stdout.read() + errmsg = pipe.stderr.read() + status = pipe.wait() + if status or errmsg: + msg = 'error executing %s: status %s' % (cmdname, status,) + if errmsg: + msg += ', error output:\n%s' % (errmsg,) + raise CommandFailedException(msg) + + +class PipeStream(object): + """A file-like object from which revision contents can be read.""" + + def __init__(self, pipe_command): + self._pipe_command_str = ' '.join(pipe_command) + self.pipe = subprocess.Popen( + pipe_command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + self.pipe.stdin.close() + + def read(self, size=None): + if size is None: + return self.pipe.stdout.read() + else: + return self.pipe.stdout.read(size) + + def close(self): + self.pipe.stdout.close() + error_output = self.pipe.stderr.read() + exit_status = self.pipe.wait() + if exit_status: + raise CommandError(self._pipe_command_str, exit_status, error_output) + + |