blob: 810631ae225feae33cfa53b62bd5ee44e1c1f6e6 [file] [log] [blame]
maruel@chromium.org4860f052011-03-25 20:34:38 +00001# coding=utf8
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Collection of subprocess wrapper functions.
6
7In theory you shouldn't need anything else in subprocess, or this module failed.
8"""
9
maruel@chromium.org45d8db02011-03-31 20:43:56 +000010from __future__ import with_statement
maruel@chromium.org65be6f62011-11-09 13:57:57 +000011import cStringIO
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000012import errno
maruel@chromium.org4860f052011-03-25 20:34:38 +000013import logging
14import os
maruel@chromium.org65be6f62011-11-09 13:57:57 +000015import Queue
maruel@chromium.org4860f052011-03-25 20:34:38 +000016import subprocess
17import sys
maruel@chromium.org4860f052011-03-25 20:34:38 +000018import time
19import threading
20
21# Constants forwarded from subprocess.
22PIPE = subprocess.PIPE
23STDOUT = subprocess.STDOUT
maruel@chromium.org421982f2011-04-01 17:38:06 +000024# Sends stdout or stderr to os.devnull.
maruel@chromium.org0d5ef242011-04-18 13:52:58 +000025VOID = object()
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000026# Error code when a process was killed because it timed out.
27TIMED_OUT = -2001
maruel@chromium.org4860f052011-03-25 20:34:38 +000028
29# Globals.
30# Set to True if you somehow need to disable this hack.
31SUBPROCESS_CLEANUP_HACKED = False
32
33
34class CalledProcessError(subprocess.CalledProcessError):
35 """Augment the standard exception with more data."""
36 def __init__(self, returncode, cmd, cwd, stdout, stderr):
37 super(CalledProcessError, self).__init__(returncode, cmd)
38 self.stdout = stdout
39 self.stderr = stderr
40 self.cwd = cwd
41
42 def __str__(self):
43 out = 'Command %s returned non-zero exit status %s' % (
44 ' '.join(self.cmd), self.returncode)
45 if self.cwd:
46 out += ' in ' + self.cwd
47 return '\n'.join(filter(None, (out, self.stdout, self.stderr)))
48
49
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000050class CygwinRebaseError(CalledProcessError):
51 """Occurs when cygwin's fork() emulation fails due to rebased dll."""
52
53
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000054## Utility functions
55
56
57def kill_pid(pid):
58 """Kills a process by its process id."""
59 try:
60 # Unable to import 'module'
maruel@chromium.orgc98c0c52011-04-06 13:39:43 +000061 # pylint: disable=E1101,F0401
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000062 import signal
63 return os.kill(pid, signal.SIGKILL)
64 except ImportError:
65 pass
66
67
68def kill_win(process):
69 """Kills a process with its windows handle.
70
71 Has no effect on other platforms.
72 """
73 try:
74 # Unable to import 'module'
75 # pylint: disable=F0401
76 import win32process
77 # Access to a protected member _handle of a client class
78 # pylint: disable=W0212
79 return win32process.TerminateProcess(process._handle, -1)
80 except ImportError:
81 pass
82
83
84def add_kill():
85 """Adds kill() method to subprocess.Popen for python <2.6"""
86 if hasattr(subprocess.Popen, 'kill'):
87 return
88
89 if sys.platform == 'win32':
90 subprocess.Popen.kill = kill_win
91 else:
92 subprocess.Popen.kill = lambda process: kill_pid(process.pid)
93
94
maruel@chromium.org4860f052011-03-25 20:34:38 +000095def hack_subprocess():
96 """subprocess functions may throw exceptions when used in multiple threads.
97
98 See http://bugs.python.org/issue1731717 for more information.
99 """
100 global SUBPROCESS_CLEANUP_HACKED
101 if not SUBPROCESS_CLEANUP_HACKED and threading.activeCount() != 1:
102 # Only hack if there is ever multiple threads.
103 # There is no point to leak with only one thread.
104 subprocess._cleanup = lambda: None
105 SUBPROCESS_CLEANUP_HACKED = True
106
107
108def get_english_env(env):
109 """Forces LANG and/or LANGUAGE to be English.
110
111 Forces encoding to utf-8 for subprocesses.
112
113 Returns None if it is unnecessary.
114 """
maruel@chromium.orgc98c0c52011-04-06 13:39:43 +0000115 if sys.platform == 'win32':
116 return None
maruel@chromium.org4860f052011-03-25 20:34:38 +0000117 env = env or os.environ
118
119 # Test if it is necessary at all.
120 is_english = lambda name: env.get(name, 'en').startswith('en')
121
122 if is_english('LANG') and is_english('LANGUAGE'):
123 return None
124
125 # Requires modifications.
126 env = env.copy()
127 def fix_lang(name):
128 if not is_english(name):
129 env[name] = 'en_US.UTF-8'
130 fix_lang('LANG')
131 fix_lang('LANGUAGE')
132 return env
133
134
135def Popen(args, **kwargs):
maruel@chromium.org57bf78d2011-09-08 18:57:33 +0000136 """Wraps subprocess.Popen() with various workarounds.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000137
maruel@chromium.org421982f2011-04-01 17:38:06 +0000138 Returns a subprocess.Popen object.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000139
maruel@chromium.org421982f2011-04-01 17:38:06 +0000140 - Forces English output since it's easier to parse the stdout if it is always
141 in English.
142 - Sets shell=True on windows by default. You can override this by forcing
143 shell parameter to a value.
144 - Adds support for VOID to not buffer when not needed.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000145
maruel@chromium.org57bf78d2011-09-08 18:57:33 +0000146 Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate
147 exceptions generated by cygwin when it fails trying to emulate fork().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000148 """
149 # Make sure we hack subprocess if necessary.
150 hack_subprocess()
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +0000151 add_kill()
maruel@chromium.org4860f052011-03-25 20:34:38 +0000152
153 env = get_english_env(kwargs.get('env'))
154 if env:
155 kwargs['env'] = env
maruel@chromium.orgf08b09c2011-04-06 13:14:27 +0000156 if kwargs.get('shell') is None:
maruel@chromium.org4860f052011-03-25 20:34:38 +0000157 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for the
158 # executable, but shell=True makes subprocess on Linux fail when it's called
159 # with a list because it only tries to execute the first item in the list.
maruel@chromium.orgf08b09c2011-04-06 13:14:27 +0000160 kwargs['shell'] = bool(sys.platform=='win32')
maruel@chromium.org4860f052011-03-25 20:34:38 +0000161
maruel@chromium.org55156552011-10-21 12:48:25 +0000162 if isinstance(args, basestring):
163 tmp_str = args
164 elif isinstance(args, (list, tuple)):
165 tmp_str = ' '.join(args)
166 else:
167 raise CalledProcessError(None, args, kwargs.get('cwd'), None, None)
maruel@chromium.org4860f052011-03-25 20:34:38 +0000168 if kwargs.get('cwd', None):
169 tmp_str += '; cwd=%s' % kwargs['cwd']
170 logging.debug(tmp_str)
maruel@chromium.org421982f2011-04-01 17:38:06 +0000171
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000172 def fix(stream):
173 if kwargs.get(stream) in (VOID, os.devnull):
174 # Replaces VOID with handle to /dev/null.
175 # Create a temporary file to workaround python's deadlock.
176 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
177 # When the pipe fills up, it will deadlock this process. Using a real file
178 # works around that issue.
179 kwargs[stream] = open(os.devnull, 'w')
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000180 if callable(kwargs.get(stream)):
181 # Callable stdout/stderr should be used only with call() wrappers.
182 kwargs[stream] = PIPE
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000183
184 fix('stdout')
185 fix('stderr')
186
187 try:
188 return subprocess.Popen(args, **kwargs)
189 except OSError, e:
190 if e.errno == errno.EAGAIN and sys.platform == 'cygwin':
191 # Convert fork() emulation failure into a CygwinRebaseError().
192 raise CygwinRebaseError(
193 e.errno,
194 args,
195 kwargs.get('cwd'),
196 None,
197 'Visit '
198 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure to '
199 'learn how to fix this error; you need to rebase your cygwin dlls')
200 # Popen() can throw OSError when cwd or args[0] doesn't exist. Let it go
201 # through
202 raise
maruel@chromium.org4860f052011-03-25 20:34:38 +0000203
204
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000205def _queue_pipe_read(pipe, name, done, dest):
206 """Queue characters read from a pipe into a queue.
207
208 Left outside the _tee_threads function to not introduce a function closure
209 to speed up variable lookup.
210 """
211 while not done.isSet():
212 data = pipe.read(1)
213 if not data:
214 break
215 dest.put((name, data))
216 dest.put(name)
217
218
219def _tee_threads(proc, timeout, start, stdin, args, kwargs):
220 """Does I/O for a process's pipes using thread.
221
222 It's the simplest and slowest implementation. Expect very slow behavior.
223
224 If there is a callback and it doesn't keep up with the calls, the timeout
225 effectiveness will be delayed accordingly.
226 """
227 # TODO(maruel): Implement a select based implementation on POSIX and a Windows
228 # one using WaitForMultipleObjects().
229 #
230 # Queue of either of <threadname> when done or (<threadname>, data).
231 # In theory we would like to limit to ~64kb items to not cause large memory
232 # usage when the callback blocks. It is not done because it slows down
233 # processing on OSX10.6 by a factor of 2x, making it even slower than Windows!
234 # Revisit this decision if it becomes a problem, e.g. crash because of
235 # memory exhaustion.
236 queue = Queue.Queue()
237 done = threading.Event()
238
239 def write_stdin():
240 stdin_io = cStringIO.StringIO(stdin)
241 while not done.isSet():
242 data = stdin_io.read(1024)
243 if data:
244 proc.stdin.write(data)
245 else:
246 proc.stdin.close()
247 break
248 queue.put('stdin')
249
250 def timeout_fn():
251 done.wait(timeout)
252 # No need to close the pipes since killing should be sufficient.
253 queue.put('timeout')
254
255 # Starts up to 4 threads:
256 # Read stdout
257 # Read stderr
258 # Write stdin
259 # Timeout
260 threads = {}
261 if timeout is not None:
262 threads['timeout'] = threading.Thread(target=timeout_fn)
263 if callable(kwargs.get('stdout')):
264 threads['stdout'] = threading.Thread(
265 target=_queue_pipe_read, args=(proc.stdout, 'stdout', done, queue))
266 if callable(kwargs.get('stderr')):
267 threads['stderr'] = threading.Thread(
268 target=_queue_pipe_read,
269 args=(proc.stderr, 'stderr', done, queue))
270 if isinstance(stdin, str):
271 threads['stdin'] = threading.Thread(target=write_stdin)
272 for t in threads.itervalues():
273 t.daemon = True
274 t.start()
275
276 timed_out = False
277 try:
278 while proc.returncode is None:
279 assert threads
280 proc.poll()
281 item = queue.get()
282 if isinstance(item, str):
283 threads[item].join()
284 del threads[item]
285 if item == 'timeout' and not timed_out and proc.poll() is None:
286 logging.debug('Timed out: killing')
287 proc.kill()
288 timed_out = True
289 if not threads:
290 # We won't be waken up anymore. Need to busy loop.
291 break
292 else:
293 kwargs[item[0]](item[1])
294 finally:
295 # Stop the threads.
296 done.set()
297 # Join threads
298 for thread in threads.itervalues():
299 thread.join()
300
301 # Flush the queue.
302 try:
303 while True:
304 item = queue.get(False)
305 if isinstance(item, str):
306 if item == 'timeout':
307 # TODO(maruel): Does it make sense at that point?
308 if not timed_out and proc.poll() is None:
309 logging.debug('Timed out: killing')
310 proc.kill()
311 timed_out = True
312 else:
313 kwargs[item[0]](item[1])
314 except Queue.Empty:
315 pass
316
317 # Get the remainder.
318 if callable(kwargs.get('stdout')):
319 data = proc.stdout.read()
320 while data:
321 kwargs['stdout'](data)
322 data = proc.stdout.read()
323 if callable(kwargs.get('stderr')):
324 data = proc.stderr.read()
325 while data:
326 kwargs['stderr'](data)
327 data = proc.stderr.read()
328
329 if proc.returncode is None:
330 # Usually happens when killed with timeout but not listening to pipes.
331 proc.wait()
332
333 if timed_out:
334 return TIMED_OUT
335
336 return proc.returncode
337
338
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000339def communicate(args, timeout=None, **kwargs):
maruel@chromium.org4860f052011-03-25 20:34:38 +0000340 """Wraps subprocess.Popen().communicate().
341
maruel@chromium.org421982f2011-04-01 17:38:06 +0000342 Returns ((stdout, stderr), returncode).
maruel@chromium.org4860f052011-03-25 20:34:38 +0000343
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000344 - The process will be killed after |timeout| seconds and returncode set to
345 TIMED_OUT.
maruel@chromium.org421982f2011-04-01 17:38:06 +0000346 - Automatically passes stdin content as input so do not specify stdin=PIPE.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000347 """
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000348 if timeout and kwargs.get('shell'):
349 raise TypeError(
350 'Using timeout and shell simultaneously will cause a process leak '
351 'since the shell will be killed instead of the child process.')
352
maruel@chromium.org4860f052011-03-25 20:34:38 +0000353 stdin = kwargs.pop('stdin', None)
354 if stdin is not None:
maruel@chromium.org0d5ef242011-04-18 13:52:58 +0000355 if stdin is VOID:
356 kwargs['stdin'] = open(os.devnull, 'r')
357 stdin = None
358 else:
maruel@chromium.org39f645f2011-04-21 00:07:53 +0000359 assert isinstance(stdin, basestring)
maruel@chromium.org0d5ef242011-04-18 13:52:58 +0000360 # When stdin is passed as an argument, use it as the actual input data and
361 # set the Popen() parameter accordingly.
362 kwargs['stdin'] = PIPE
maruel@chromium.org4860f052011-03-25 20:34:38 +0000363
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000364 start = time.time()
365 proc = Popen(args, **kwargs)
366 need_buffering = (timeout or
367 callable(kwargs.get('stdout')) or callable(kwargs.get('stderr')))
368
369 if not need_buffering:
maruel@chromium.org4860f052011-03-25 20:34:38 +0000370 # Normal workflow.
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000371 if stdin not in (None, VOID):
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000372 return proc.communicate(stdin), proc.returncode
maruel@chromium.org4860f052011-03-25 20:34:38 +0000373 else:
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000374 return proc.communicate(), proc.returncode
375
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000376 stdout = None
377 stderr = None
378 # Convert to a lambda to workaround python's deadlock.
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000379 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
maruel@chromium.org65be6f62011-11-09 13:57:57 +0000380 # When the pipe fills up, it will deadlock this process. Using a thread
381 # works around that issue. No need for thread safe function since the call
382 # backs are guaranteed to be called from the main thread.
383 if kwargs.get('stdout') == PIPE:
384 stdout = []
385 kwargs['stdout'] = stdout.append
386 if kwargs.get('stderr') == PIPE:
387 stderr = []
388 kwargs['stderr'] = stderr.append
389 returncode = _tee_threads(proc, timeout, start, stdin, args, kwargs)
390 if not stdout is None:
391 stdout = ''.join(stdout)
392 if not stderr is None:
393 stderr = ''.join(stderr)
394 return (stdout, stderr), returncode
maruel@chromium.org4860f052011-03-25 20:34:38 +0000395
396
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000397def call(args, **kwargs):
398 """Emulates subprocess.call().
399
400 Automatically convert stdout=PIPE or stderr=PIPE to VOID.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000401 In no case they can be returned since no code path raises
402 subprocess2.CalledProcessError.
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000403 """
404 if kwargs.get('stdout') == PIPE:
405 kwargs['stdout'] = VOID
406 if kwargs.get('stderr') == PIPE:
407 kwargs['stderr'] = VOID
408 return communicate(args, **kwargs)[1]
409
410
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000411def check_call_out(args, **kwargs):
maruel@chromium.org421982f2011-04-01 17:38:06 +0000412 """Improved version of subprocess.check_call().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000413
maruel@chromium.org421982f2011-04-01 17:38:06 +0000414 Returns (stdout, stderr), unlike subprocess.check_call().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000415 """
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000416 out, returncode = communicate(args, **kwargs)
maruel@chromium.org4860f052011-03-25 20:34:38 +0000417 if returncode:
418 raise CalledProcessError(
419 returncode, args, kwargs.get('cwd'), out[0], out[1])
420 return out
421
422
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000423def check_call(args, **kwargs):
424 """Emulate subprocess.check_call()."""
425 check_call_out(args, **kwargs)
426 return 0
427
428
maruel@chromium.org4860f052011-03-25 20:34:38 +0000429def capture(args, **kwargs):
430 """Captures stdout of a process call and returns it.
431
maruel@chromium.org421982f2011-04-01 17:38:06 +0000432 Returns stdout.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000433
maruel@chromium.org421982f2011-04-01 17:38:06 +0000434 - Discards returncode.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000435 - Blocks stdin by default if not specified since no output will be visible.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000436 """
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000437 kwargs.setdefault('stdin', VOID)
438
439 # Like check_output, deny the caller from using stdout arg.
440 return communicate(args, stdout=PIPE, **kwargs)[0][0]
maruel@chromium.org4860f052011-03-25 20:34:38 +0000441
442
443def check_output(args, **kwargs):
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000444 """Emulates subprocess.check_output().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000445
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000446 Captures stdout of a process call and returns stdout only.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000447
maruel@chromium.org421982f2011-04-01 17:38:06 +0000448 - Throws if return code is not 0.
449 - Works even prior to python 2.7.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000450 - Blocks stdin by default if not specified since no output will be visible.
451 - As per doc, "The stdout argument is not allowed as it is used internally."
maruel@chromium.org4860f052011-03-25 20:34:38 +0000452 """
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000453 kwargs.setdefault('stdin', VOID)
454 return check_call_out(args, stdout=PIPE, **kwargs)[0]