Extract functions generate_edits_from_blocks() and write_edits().
[cvs2svn.git] / cvs2svn_lib / process.py
blob072422b3cbc5a82d2e63f0e089ced2c3d487ca59
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2008 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains generic utilities used by cvs2svn."""
20 import subprocess
22 from cvs2svn_lib.common import FatalError
23 from cvs2svn_lib.common import CommandError
26 def call_command(command, **kw):
27 """Call the specified command, checking that it exits successfully.
29 Raise a FatalError if the command cannot be executed, or if it exits
30 with a non-zero exit code. Pass KW as keyword arguments to
31 subprocess.call()."""
33 try:
34 retcode = subprocess.call(command, **kw)
35 if retcode < 0:
36 raise FatalError(
37 'Command terminated by signal %d: "%s"'
38 % (-retcode, ' '.join(command),)
40 elif retcode > 0:
41 raise FatalError(
42 'Command failed with return code %d: "%s"'
43 % (retcode, ' '.join(command),)
45 except OSError, e:
46 raise FatalError(
47 'Command execution failed (%s): "%s"'
48 % (e, ' '.join(command),)
52 class CommandFailedException(Exception):
53 """Exception raised if check_command_runs() fails."""
55 pass
58 def check_command_runs(cmd, cmdname):
59 """Check whether the command CMD can be executed without errors.
61 CMD is a list or string, as accepted by subprocess.Popen(). CMDNAME
62 is the name of the command as it should be included in exception
63 error messages.
65 This function checks three things: (1) the command can be run
66 without throwing an OSError; (2) it exits with status=0; (3) it
67 doesn't output anything to stderr. If any of these conditions is
68 not met, raise a CommandFailedException describing the problem."""
70 try:
71 pipe = subprocess.Popen(
72 cmd,
73 stdin=subprocess.PIPE,
74 stdout=subprocess.PIPE,
75 stderr=subprocess.PIPE,
77 except OSError, e:
78 raise CommandFailedException('error executing %s: %s' % (cmdname, e,))
79 pipe.stdin.close()
80 pipe.stdout.read()
81 errmsg = pipe.stderr.read()
82 status = pipe.wait()
83 if status or errmsg:
84 msg = 'error executing %s: status %s' % (cmdname, status,)
85 if errmsg:
86 msg += ', error output:\n%s' % (errmsg,)
87 raise CommandFailedException(msg)
90 class PipeStream(object):
91 """A file-like object from which revision contents can be read."""
93 def __init__(self, pipe_command):
94 self._pipe_command_str = ' '.join(pipe_command)
95 self.pipe = subprocess.Popen(
96 pipe_command,
97 stdin=subprocess.PIPE,
98 stdout=subprocess.PIPE,
99 stderr=subprocess.PIPE,
101 self.pipe.stdin.close()
103 def read(self, size=None):
104 if size is None:
105 return self.pipe.stdout.read()
106 else:
107 return self.pipe.stdout.read(size)
109 def close(self):
110 self.pipe.stdout.close()
111 error_output = self.pipe.stderr.read()
112 exit_status = self.pipe.wait()
113 if exit_status or error_output:
114 raise CommandError(self._pipe_command_str, exit_status, error_output)