Moved _clean_symbolic_name() to common module.
[cvs2svn.git] / cvs2svn_lib / process.py
blobeba1a8dcf30f57e1bdb4fcf58025c67c2bff1526
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2006 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 sys
21 import os
23 from boolean import *
24 from common import FatalError
27 # ============================================================================
28 # This code is copied with a few modifications from:
29 # subversion/subversion/bindings/swig/python/svn/core.py
31 if sys.platform == "win32":
32 import re
33 _escape_shell_arg_re = re.compile(r'(\\+)(\"|$)')
35 def escape_shell_arg(arg):
36 # The (very strange) parsing rules used by the C runtime library are
37 # described at:
38 # http://msdn.microsoft.com/library/en-us/vclang/html/_pluslang_Parsing_C.2b2b_.Command.2d.Line_Arguments.asp
40 # double up slashes, but only if they are followed by a quote character
41 arg = re.sub(_escape_shell_arg_re, r'\1\1\2', arg)
43 # surround by quotes and escape quotes inside
44 arg = '"' + arg.replace('"', '"^""') + '"'
45 return arg
48 def argv_to_command_string(argv):
49 """Flatten a list of command line arguments into a command string.
51 The resulting command string is expected to be passed to the system
52 shell which os functions like popen() and system() invoke internally.
53 """
55 # According cmd's usage notes (cmd /?), it parses the command line by
56 # "seeing if the first character is a quote character and if so, stripping
57 # the leading character and removing the last quote character."
58 # So to prevent the argument string from being changed we add an extra set
59 # of quotes around it here.
60 return '"' + ' '.join(map(escape_shell_arg, argv)) + '"'
62 else:
63 def escape_shell_arg(arg):
64 return "'" + arg.replace("'", "'\\''") + "'"
66 def argv_to_command_string(argv):
67 """Flatten a list of command line arguments into a command string.
69 The resulting command string is expected to be passed to the system
70 shell which os functions like popen() and system() invoke internally.
71 """
73 return ' '.join(map(escape_shell_arg, argv))
76 # ============================================================================
79 # Opening pipes was a mess before Python 2.4, because some methods did
80 # not exist on some platforms, and some behaved differenly on other.
81 # Python 2.4 solved this by adding the subprocess module, but since we
82 # cannot require such a new version, we cannot use it directly, but
83 # must implement a simplified Popen using the best means neccessary.
85 # The SimplePopen class only has the following members and methods, all
86 # behaving as documented in the subprocess.Popen class:
87 # - stdin
88 # - stdout
89 # - stderr
90 # - wait
91 try:
92 # First try subprocess.Popen...
93 import subprocess
94 class SimplePopen:
95 def __init__(self, cmd, capture_stderr):
96 if capture_stderr:
97 stderr = subprocess.PIPE
98 else:
99 stderr = None
100 self._popen = subprocess.Popen(cmd, stdin=subprocess.PIPE,
101 stdout=subprocess.PIPE, stderr=stderr)
102 self.stdin = self._popen.stdin
103 self.stdout = self._popen.stdout
104 if capture_stderr:
105 self.stderr = self._popen.stderr
106 self.wait = self._popen.wait
107 except ImportError:
108 import popen2
109 if hasattr(popen2, 'Popen3'):
110 # ...then try popen2.Popen3...
111 class SimplePopen:
112 def __init__(self, cmd, capture_stderr):
113 self._popen3 = popen2.Popen3(cmd, capture_stderr)
114 self.stdin = self._popen3.tochild
115 self.stdout = self._popen3.fromchild
116 if capture_stderr:
117 self.stderr = self._popen3.childerr
118 self.wait = self._popen3.wait
119 else:
120 # ...and if all fails, use popen2.popen3...
121 class SimplePopen:
122 def __init__(self, cmd, capture_stderr):
123 if type(cmd) != types.StringType:
124 cmd = argv_to_command_string(cmd)
125 self.stdout, self.stdin, self.stderr = popen2.popen3(cmd, mode='b')
126 def wait(self):
127 return self.stdout.close() or self.stdin.close() or \
128 self.stderr.close()
131 def run_command(command):
132 if os.system(command):
133 raise FatalError('Command failed: "%s"' % (command,))
136 class CommandFailedException(Exception):
137 """Exception raised if check_command_runs() fails."""
139 pass
142 def check_command_runs(cmd, cmdname):
143 """Check whether the command CMD can be executed without errors.
145 CMD is a list or string, as accepted by SimplePopen. CMDNAME is the
146 name of the command as it should be included in exception error
147 messages.
149 This function checks three things: (1) the command can be run
150 without throwing an OSError; (2) it exits with status=0; (3) it
151 doesn't output anything to stderr. If any of these conditions is
152 not met, raise a CommandFailedException describing the problem."""
154 try:
155 pipe = SimplePopen(cmd, True)
156 except OSError, e:
157 raise CommandFailedException('error executing %s: %s' % (cmdname, e,))
158 pipe.stdin.close()
159 pipe.stdout.read()
160 errmsg = pipe.stderr.read()
161 status = pipe.wait()
162 if status or errmsg:
163 msg = 'error executing %s: status %s' % (cmdname, status,)
164 if errmsg:
165 msg += ', error output:\n%s' % (errmsg,)
166 raise CommandFailedException(msg)