aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'cvs2svn_lib/process.py')
-rw-r--r--cvs2svn_lib/process.py116
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)
+
+