Use Popen.communicate() instead of own, fragile code.
[cvs2svn.git] / cvs2svn_lib / process.py
blob55b903fd7691ea1cdb275842187ab88d02bd949b
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
24 from cvs2svn_lib.log import logger
27 def call_command(command, **kw):
28 """Call the specified command, checking that it exits successfully.
30 Raise a FatalError if the command cannot be executed, or if it exits
31 with a non-zero exit code. Pass KW as keyword arguments to
32 subprocess.call()."""
34 logger.debug('Running command %r' % (command,))
35 try:
36 retcode = subprocess.call(command, **kw)
37 if retcode < 0:
38 raise FatalError(
39 'Command terminated by signal %d: "%s"'
40 % (-retcode, ' '.join(command),)
42 elif retcode > 0:
43 raise FatalError(
44 'Command failed with return code %d: "%s"'
45 % (retcode, ' '.join(command),)
47 except OSError, e:
48 raise FatalError(
49 'Command execution failed (%s): "%s"'
50 % (e, ' '.join(command),)
54 class CommandFailedException(Exception):
55 """Exception raised if check_command_runs() fails."""
57 pass
60 def check_command_runs(command, commandname):
61 """Check whether the command CMD can be executed without errors.
63 CMD is a list or string, as accepted by subprocess.Popen(). CMDNAME
64 is the name of the command as it should be included in exception
65 error messages.
67 This function checks three things: (1) the command can be run
68 without throwing an OSError; (2) it exits with status=0; (3) it
69 doesn't output anything to stderr. If any of these conditions is
70 not met, raise a CommandFailedException describing the problem."""
72 logger.debug('Running command %r' % (command,))
73 try:
74 pipe = subprocess.Popen(
75 command,
76 stdin=subprocess.PIPE,
77 stdout=subprocess.PIPE,
78 stderr=subprocess.PIPE,
80 except OSError, e:
81 raise CommandFailedException('error executing %s: %s' % (commandname, e,))
82 (stdout, stderr) = pipe.communicate()
83 if pipe.returncode or stderr:
84 msg = 'error executing %s; returncode=%s' % (commandname, pipe.returncode,)
85 if stderr:
86 msg += ', error output:\n%s' % (stderr,)
87 raise CommandFailedException(msg)
90 def get_command_output(command):
91 """Run COMMAND and return its stdout.
93 COMMAND is a list of strings. Run the command and return its stdout
94 as a string. If the command exits with a nonzero return code or
95 writes something to stderr, raise a CommandError."""
97 """A file-like object from which revision contents can be read."""
99 logger.debug('Running command %r' % (command,))
100 pipe = subprocess.Popen(
101 command,
102 stdin=subprocess.PIPE,
103 stdout=subprocess.PIPE,
104 stderr=subprocess.PIPE,
106 (stdout, stderr) = pipe.communicate()
107 if pipe.returncode or stderr:
108 raise CommandError(' '.join(command), pipe.returncode, stderr)
109 return stdout