1 # Copyright (C) 2009-2010 W. Trevor King <wking@drexel.edu>
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # The author may be contacted at <wking@drexel.edu> on the Internet, or
17 # write to Trevor King, Drexel University, Physics Dept., 3141 Chestnut St.,
18 # Philadelphia PA 19104, USA.
20 """Functions for running external commands in subprocesses.
23 from subprocess import Popen, PIPE
27 class CommandError(Exception):
28 """Represent errors in command execution.
30 Instances are picklable (for passing through `multiprocessing.Queue`\s).
33 >>> a = CommandError('somefunc', 1, '', 'could not find "somefunc"')
34 >>> x = pickle.dumps(a)
35 >>> b = pickle.loads(x)
38 could not find "somefunc"
42 >>> print repr(b) # doctest: +NORMALIZE_WHITESPACE
43 CommandError(command='somefunc', status=1, stdout='',
44 stderr='could not find "somefunc"')
46 def __init__(self, command=None, status=None, stdout=None, stderr=None):
47 self.command = command
51 Exception.__init__(self, self.__str__())
53 def __getstate__(self):
56 def __setstate__(self, data):
57 self.__dict__.update(data)
61 "Command failed (%s):\n %s\n" % (self.status, self.stderr),
62 "while executing\n %s" % self.command,
67 self.__class__.__name__,
68 ', '.join(['%s=%s' % (attr, repr(getattr(self, attr)))
69 for attr in ['command', 'status', 'stdout', 'stderr']]))
72 def invoke(cmd_string, stdin=None, expect=(0,), cwd=None, verbose=False):
74 expect should be a tuple of allowed exit codes. cwd should be
75 the directory from which the command will be executed.
80 print >> sys.stderr, "%s$ %s" % (cwd, cmd_string)
82 if sys.platform != "win32" and False:
83 q = Popen(cmd_string, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=cwd)
85 # win32 don't have os.execvp() so have to run command in a shell
86 q = Popen(cmd_string, stdin=PIPE, stdout=PIPE, stderr=PIPE,
89 raise CommandError(cmd_string, status=e.args[0], stdout="", stderr=e)
90 stdout,stderr = q.communicate(input=stdin)
93 print >> sys.stderr, "%d\n%s%s" % (status, stdout, stderr)
94 if status not in expect:
95 raise CommandError(cmd_string, status, stdout, stderr)
96 return status, stdout, stderr