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."""
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":
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
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('"', '"^""') + '"'
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.
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
)) + '"'
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.
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:
92 # First try subprocess.Popen...
95 def __init__(self
, cmd
, capture_stderr
):
97 stderr
= subprocess
.PIPE
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
105 self
.stderr
= self
._popen
.stderr
106 self
.wait
= self
._popen
.wait
109 if hasattr(popen2
, 'Popen3'):
110 # ...then try popen2.Popen3...
112 def __init__(self
, cmd
, capture_stderr
):
113 self
._popen
3 = popen2
.Popen3(cmd
, capture_stderr
)
114 self
.stdin
= self
._popen
3.tochild
115 self
.stdout
= self
._popen
3.fromchild
117 self
.stderr
= self
._popen
3.childerr
118 self
.wait
= self
._popen
3.wait
120 # ...and if all fails, use popen2.popen3...
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')
127 return self
.stdout
.close() or self
.stdin
.close() or \
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."""
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
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."""
155 pipe
= SimplePopen(cmd
, True)
157 raise CommandFailedException('error executing %s: %s' % (cmdname
, e
,))
160 errmsg
= pipe
.stderr
.read()
163 msg
= 'error executing %s: status %s' % (cmdname
, status
,)
165 msg
+= ', error output:\n%s' % (errmsg
,)
166 raise CommandFailedException(msg
)