blob: d899e4729bc88486f9248044a5bae2f4f5a29b95 [file] [log] [blame]
maruel@chromium.org4860f052011-03-25 20:34:38 +00001# coding=utf8
maruel@chromium.org4f6852c2012-04-20 20:39:20 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
maruel@chromium.org4860f052011-03-25 20:34:38 +00003# 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.org94c712f2011-12-01 15:04:57 +000010import cStringIO
John Budorick9875e182018-12-05 22:57:31 +000011import codecs
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000012import errno
maruel@chromium.org4860f052011-03-25 20:34:38 +000013import logging
14import os
maruel@chromium.org94c712f2011-12-01 15:04: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
John Budorick9875e182018-12-05 22:57:31 +000021# Cache the string-escape codec to ensure subprocess can find it later.
22# See crbug.com/912292#c2 for context.
23codecs.lookup('string-escape')
maruel@chromium.orga8e81632011-12-01 00:35:24 +000024
maruel@chromium.org4860f052011-03-25 20:34:38 +000025# Constants forwarded from subprocess.
26PIPE = subprocess.PIPE
27STDOUT = subprocess.STDOUT
maruel@chromium.org421982f2011-04-01 17:38:06 +000028# Sends stdout or stderr to os.devnull.
maruel@chromium.org0d5ef242011-04-18 13:52:58 +000029VOID = object()
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000030# Error code when a process was killed because it timed out.
31TIMED_OUT = -2001
maruel@chromium.org4860f052011-03-25 20:34:38 +000032
33# Globals.
34# Set to True if you somehow need to disable this hack.
35SUBPROCESS_CLEANUP_HACKED = False
36
37
38class CalledProcessError(subprocess.CalledProcessError):
39 """Augment the standard exception with more data."""
40 def __init__(self, returncode, cmd, cwd, stdout, stderr):
tandrii@chromium.orgc15fe572014-09-19 11:51:43 +000041 super(CalledProcessError, self).__init__(returncode, cmd, output=stdout)
42 self.stdout = self.output # for backward compatibility.
maruel@chromium.org4860f052011-03-25 20:34:38 +000043 self.stderr = stderr
44 self.cwd = cwd
45
46 def __str__(self):
sbc@chromium.org217330f2015-06-01 22:10:14 +000047 out = 'Command %r returned non-zero exit status %s' % (
maruel@chromium.org4860f052011-03-25 20:34:38 +000048 ' '.join(self.cmd), self.returncode)
49 if self.cwd:
50 out += ' in ' + self.cwd
51 return '\n'.join(filter(None, (out, self.stdout, self.stderr)))
52
53
maruel@chromium.org1d9f6292011-04-07 14:15:36 +000054class CygwinRebaseError(CalledProcessError):
55 """Occurs when cygwin's fork() emulation fails due to rebased dll."""
56
57
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000058## Utility functions
59
60
61def kill_pid(pid):
62 """Kills a process by its process id."""
63 try:
64 # Unable to import 'module'
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -080065 # pylint: disable=no-member,F0401
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000066 import signal
67 return os.kill(pid, signal.SIGKILL)
68 except ImportError:
69 pass
70
71
72def kill_win(process):
73 """Kills a process with its windows handle.
74
75 Has no effect on other platforms.
76 """
77 try:
78 # Unable to import 'module'
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -080079 # pylint: disable=import-error
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000080 import win32process
81 # Access to a protected member _handle of a client class
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -080082 # pylint: disable=protected-access
maruel@chromium.orgfb3d3242011-04-01 14:03:08 +000083 return win32process.TerminateProcess(process._handle, -1)
84 except ImportError:
85 pass
86
87
88def add_kill():
89 """Adds kill() method to subprocess.Popen for python <2.6"""
90 if hasattr(subprocess.Popen, 'kill'):
91 return
92
93 if sys.platform == 'win32':
94 subprocess.Popen.kill = kill_win
95 else:
96 subprocess.Popen.kill = lambda process: kill_pid(process.pid)
97
98
maruel@chromium.org4860f052011-03-25 20:34:38 +000099def hack_subprocess():
100 """subprocess functions may throw exceptions when used in multiple threads.
101
102 See http://bugs.python.org/issue1731717 for more information.
103 """
104 global SUBPROCESS_CLEANUP_HACKED
105 if not SUBPROCESS_CLEANUP_HACKED and threading.activeCount() != 1:
106 # Only hack if there is ever multiple threads.
107 # There is no point to leak with only one thread.
108 subprocess._cleanup = lambda: None
109 SUBPROCESS_CLEANUP_HACKED = True
110
111
112def get_english_env(env):
113 """Forces LANG and/or LANGUAGE to be English.
114
115 Forces encoding to utf-8 for subprocesses.
116
117 Returns None if it is unnecessary.
118 """
maruel@chromium.orgc98c0c52011-04-06 13:39:43 +0000119 if sys.platform == 'win32':
120 return None
maruel@chromium.org4860f052011-03-25 20:34:38 +0000121 env = env or os.environ
122
123 # Test if it is necessary at all.
124 is_english = lambda name: env.get(name, 'en').startswith('en')
125
126 if is_english('LANG') and is_english('LANGUAGE'):
127 return None
128
129 # Requires modifications.
130 env = env.copy()
131 def fix_lang(name):
132 if not is_english(name):
133 env[name] = 'en_US.UTF-8'
134 fix_lang('LANG')
135 fix_lang('LANGUAGE')
136 return env
137
138
szager@chromium.org12b07e72013-05-03 22:06:34 +0000139class NagTimer(object):
140 """
141 Triggers a callback when a time interval passes without an event being fired.
142
143 For example, the event could be receiving terminal output from a subprocess;
144 and the callback could print a warning to stderr that the subprocess appeared
145 to be hung.
146 """
147 def __init__(self, interval, cb):
148 self.interval = interval
149 self.cb = cb
150 self.timer = threading.Timer(self.interval, self.fn)
151 self.last_output = self.previous_last_output = 0
152
153 def start(self):
154 self.last_output = self.previous_last_output = time.time()
155 self.timer.start()
156
157 def event(self):
158 self.last_output = time.time()
159
160 def fn(self):
161 now = time.time()
162 if self.last_output == self.previous_last_output:
163 self.cb(now - self.previous_last_output)
164 # Use 0.1 fudge factor, just in case
165 # (self.last_output - now) is very close to zero.
166 sleep_time = (self.last_output - now - 0.1) % self.interval
167 self.previous_last_output = self.last_output
168 self.timer = threading.Timer(sleep_time + 0.1, self.fn)
169 self.timer.start()
170
171 def cancel(self):
172 self.timer.cancel()
173
174
maruel@google.comef77f9e2011-11-24 15:24:02 +0000175class Popen(subprocess.Popen):
maruel@chromium.org57bf78d2011-09-08 18:57:33 +0000176 """Wraps subprocess.Popen() with various workarounds.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000177
maruel@chromium.org421982f2011-04-01 17:38:06 +0000178 - Forces English output since it's easier to parse the stdout if it is always
179 in English.
180 - Sets shell=True on windows by default. You can override this by forcing
181 shell parameter to a value.
182 - Adds support for VOID to not buffer when not needed.
maruel@chromium.orgdd9837f2011-11-30 01:55:22 +0000183 - Adds self.start property.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000184
maruel@chromium.org57bf78d2011-09-08 18:57:33 +0000185 Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate
186 exceptions generated by cygwin when it fails trying to emulate fork().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000187 """
torne@chromium.org434e7902015-09-15 09:57:01 +0000188 # subprocess.Popen.__init__() is not threadsafe; there is a race between
189 # creating the exec-error pipe for the child and setting it to CLOEXEC during
190 # which another thread can fork and cause the pipe to be inherited by its
191 # descendents, which will cause the current Popen to hang until all those
192 # descendents exit. Protect this with a lock so that only one fork/exec can
193 # happen at a time.
194 popen_lock = threading.Lock()
195
maruel@google.comef77f9e2011-11-24 15:24:02 +0000196 def __init__(self, args, **kwargs):
197 # Make sure we hack subprocess if necessary.
198 hack_subprocess()
199 add_kill()
maruel@chromium.org4860f052011-03-25 20:34:38 +0000200
maruel@google.comef77f9e2011-11-24 15:24:02 +0000201 env = get_english_env(kwargs.get('env'))
202 if env:
203 kwargs['env'] = env
204 if kwargs.get('shell') is None:
205 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
206 # the executable, but shell=True makes subprocess on Linux fail when it's
207 # called with a list because it only tries to execute the first item in
208 # the list.
209 kwargs['shell'] = bool(sys.platform=='win32')
maruel@chromium.org4860f052011-03-25 20:34:38 +0000210
maruel@google.comef77f9e2011-11-24 15:24:02 +0000211 if isinstance(args, basestring):
212 tmp_str = args
213 elif isinstance(args, (list, tuple)):
214 tmp_str = ' '.join(args)
215 else:
216 raise CalledProcessError(None, args, kwargs.get('cwd'), None, None)
217 if kwargs.get('cwd', None):
218 tmp_str += '; cwd=%s' % kwargs['cwd']
219 logging.debug(tmp_str)
maruel@chromium.org421982f2011-04-01 17:38:06 +0000220
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000221 self.stdout_cb = None
222 self.stderr_cb = None
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000223 self.stdin_is_void = False
224 self.stdout_is_void = False
225 self.stderr_is_void = False
szager@chromium.orge0558e62013-05-02 02:48:51 +0000226 self.cmd_str = tmp_str
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000227
228 if kwargs.get('stdin') is VOID:
229 kwargs['stdin'] = open(os.devnull, 'r')
230 self.stdin_is_void = True
231
232 for stream in ('stdout', 'stderr'):
maruel@google.comef77f9e2011-11-24 15:24:02 +0000233 if kwargs.get(stream) in (VOID, os.devnull):
maruel@google.comef77f9e2011-11-24 15:24:02 +0000234 kwargs[stream] = open(os.devnull, 'w')
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000235 setattr(self, stream + '_is_void', True)
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000236 if callable(kwargs.get(stream)):
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000237 setattr(self, stream + '_cb', kwargs[stream])
238 kwargs[stream] = PIPE
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000239
maruel@chromium.orgdd9837f2011-11-30 01:55:22 +0000240 self.start = time.time()
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000241 self.timeout = None
szager@chromium.orge0558e62013-05-02 02:48:51 +0000242 self.nag_timer = None
szager@chromium.org12b07e72013-05-03 22:06:34 +0000243 self.nag_max = None
maruel@chromium.orga8e81632011-12-01 00:35:24 +0000244 self.shell = kwargs.get('shell', None)
maruel@chromium.org14e37ad2011-11-30 20:26:16 +0000245 # Silence pylint on MacOSX
246 self.returncode = None
maruel@chromium.orga8e81632011-12-01 00:35:24 +0000247
maruel@google.comef77f9e2011-11-24 15:24:02 +0000248 try:
torne@chromium.org434e7902015-09-15 09:57:01 +0000249 with self.popen_lock:
250 super(Popen, self).__init__(args, **kwargs)
maruel@google.comef77f9e2011-11-24 15:24:02 +0000251 except OSError, e:
252 if e.errno == errno.EAGAIN and sys.platform == 'cygwin':
253 # Convert fork() emulation failure into a CygwinRebaseError().
254 raise CygwinRebaseError(
255 e.errno,
256 args,
257 kwargs.get('cwd'),
258 None,
259 'Visit '
260 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure '
261 'to learn how to fix this error; you need to rebase your cygwin '
262 'dlls')
luqui@chromium.org7f627a92014-03-28 00:57:44 +0000263 # Popen() can throw OSError when cwd or args[0] doesn't exist.
pgervais@chromium.orgfb653b62014-04-29 17:29:18 +0000264 raise OSError('Execution failed with error: %s.\n'
265 'Check that %s or %s exist and have execution permission.'
266 % (str(e), kwargs.get('cwd'), args[0]))
maruel@chromium.org4860f052011-03-25 20:34:38 +0000267
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800268 def _tee_threads(self, input): # pylint: disable=redefined-builtin
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000269 """Does I/O for a process's pipes using threads.
270
271 It's the simplest and slowest implementation. Expect very slow behavior.
272
273 If there is a callback and it doesn't keep up with the calls, the timeout
274 effectiveness will be delayed accordingly.
275 """
276 # Queue of either of <threadname> when done or (<threadname>, data). In
277 # theory we would like to limit to ~64kb items to not cause large memory
278 # usage when the callback blocks. It is not done because it slows down
279 # processing on OSX10.6 by a factor of 2x, making it even slower than
280 # Windows! Revisit this decision if it becomes a problem, e.g. crash
281 # because of memory exhaustion.
282 queue = Queue.Queue()
283 done = threading.Event()
szager@chromium.org12b07e72013-05-03 22:06:34 +0000284 nag = None
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000285
286 def write_stdin():
287 try:
288 stdin_io = cStringIO.StringIO(input)
289 while True:
290 data = stdin_io.read(1024)
291 if data:
292 self.stdin.write(data)
293 else:
294 self.stdin.close()
295 break
296 finally:
297 queue.put('stdin')
298
299 def _queue_pipe_read(pipe, name):
300 """Queues characters read from a pipe into a queue."""
301 try:
302 while True:
303 data = pipe.read(1)
304 if not data:
305 break
szager@chromium.org12b07e72013-05-03 22:06:34 +0000306 if nag:
307 nag.event()
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000308 queue.put((name, data))
309 finally:
310 queue.put(name)
311
312 def timeout_fn():
313 try:
314 done.wait(self.timeout)
315 finally:
316 queue.put('timeout')
317
318 def wait_fn():
319 try:
320 self.wait()
321 finally:
322 queue.put('wait')
323
324 # Starts up to 5 threads:
325 # Wait for the process to quit
326 # Read stdout
327 # Read stderr
328 # Write stdin
329 # Timeout
330 threads = {
331 'wait': threading.Thread(target=wait_fn),
332 }
333 if self.timeout is not None:
334 threads['timeout'] = threading.Thread(target=timeout_fn)
335 if self.stdout_cb:
336 threads['stdout'] = threading.Thread(
337 target=_queue_pipe_read, args=(self.stdout, 'stdout'))
338 if self.stderr_cb:
339 threads['stderr'] = threading.Thread(
340 target=_queue_pipe_read, args=(self.stderr, 'stderr'))
341 if input:
342 threads['stdin'] = threading.Thread(target=write_stdin)
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000343 elif self.stdin:
344 # Pipe but no input, make sure it's closed.
345 self.stdin.close()
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000346 for t in threads.itervalues():
347 t.start()
348
szager@chromium.orge0558e62013-05-02 02:48:51 +0000349 if self.nag_timer:
szager@chromium.org12b07e72013-05-03 22:06:34 +0000350 def _nag_cb(elapsed):
351 logging.warn(' No output for %.0f seconds from command:' % elapsed)
352 logging.warn(' %s' % self.cmd_str)
353 if (self.nag_max and
354 int('%.0f' % (elapsed / self.nag_timer)) >= self.nag_max):
355 queue.put('timeout')
356 done.set() # Must do this so that timeout thread stops waiting.
357 nag = NagTimer(self.nag_timer, _nag_cb)
358 nag.start()
szager@chromium.orge0558e62013-05-02 02:48:51 +0000359
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000360 timed_out = False
361 try:
362 # This thread needs to be optimized for speed.
363 while threads:
364 item = queue.get()
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +0000365 if item[0] == 'stdout':
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000366 self.stdout_cb(item[1])
maruel@chromium.orgcd8d8e12012-10-03 17:16:25 +0000367 elif item[0] == 'stderr':
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000368 self.stderr_cb(item[1])
369 else:
370 # A thread terminated.
szager@chromium.org12b07e72013-05-03 22:06:34 +0000371 if item in threads:
372 threads[item].join()
373 del threads[item]
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000374 if item == 'wait':
375 # Terminate the timeout thread if necessary.
376 done.set()
377 elif item == 'timeout' and not timed_out and self.poll() is None:
szager@chromium.org12b07e72013-05-03 22:06:34 +0000378 logging.debug('Timed out after %.0fs: killing' % (
379 time.time() - self.start))
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000380 self.kill()
381 timed_out = True
382 finally:
383 # Stop the threads.
384 done.set()
szager@chromium.org12b07e72013-05-03 22:06:34 +0000385 if nag:
386 nag.cancel()
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000387 if 'wait' in threads:
388 # Accelerate things, otherwise it would hang until the child process is
389 # done.
390 logging.debug('Killing child because of an exception')
391 self.kill()
392 # Join threads.
393 for thread in threads.itervalues():
394 thread.join()
395 if timed_out:
396 self.returncode = TIMED_OUT
397
Quinten Yearsleyb2cc4a92016-12-15 13:53:26 -0800398 # pylint: disable=arguments-differ,W0622
szager@chromium.org12b07e72013-05-03 22:06:34 +0000399 def communicate(self, input=None, timeout=None, nag_timer=None,
400 nag_max=None):
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000401 """Adds timeout and callbacks support.
402
403 Returns (stdout, stderr) like subprocess.Popen().communicate().
404
405 - The process will be killed after |timeout| seconds and returncode set to
406 TIMED_OUT.
szager@chromium.orge0558e62013-05-02 02:48:51 +0000407 - If the subprocess runs for |nag_timer| seconds without producing terminal
408 output, print a warning to stderr.
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000409 """
410 self.timeout = timeout
szager@chromium.orge0558e62013-05-02 02:48:51 +0000411 self.nag_timer = nag_timer
szager@chromium.org12b07e72013-05-03 22:06:34 +0000412 self.nag_max = nag_max
szager@chromium.orge0558e62013-05-02 02:48:51 +0000413 if (not self.timeout and not self.nag_timer and
414 not self.stdout_cb and not self.stderr_cb):
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000415 return super(Popen, self).communicate(input)
416
417 if self.timeout and self.shell:
418 raise TypeError(
419 'Using timeout and shell simultaneously will cause a process leak '
420 'since the shell will be killed instead of the child process.')
421
422 stdout = None
423 stderr = None
424 # Convert to a lambda to workaround python's deadlock.
425 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000426 # When the pipe fills up, it would deadlock this process.
427 if self.stdout and not self.stdout_cb and not self.stdout_is_void:
428 stdout = []
429 self.stdout_cb = stdout.append
430 if self.stderr and not self.stderr_cb and not self.stderr_is_void:
431 stderr = []
432 self.stderr_cb = stderr.append
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000433 self._tee_threads(input)
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000434 if stdout is not None:
435 stdout = ''.join(stdout)
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000436 if stderr is not None:
437 stderr = ''.join(stderr)
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000438 return (stdout, stderr)
439
maruel@chromium.org4860f052011-03-25 20:34:38 +0000440
szager@chromium.org12b07e72013-05-03 22:06:34 +0000441def communicate(args, timeout=None, nag_timer=None, nag_max=None, **kwargs):
maruel@chromium.orgdd9837f2011-11-30 01:55:22 +0000442 """Wraps subprocess.Popen().communicate() and add timeout support.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000443
maruel@chromium.org421982f2011-04-01 17:38:06 +0000444 Returns ((stdout, stderr), returncode).
maruel@chromium.org4860f052011-03-25 20:34:38 +0000445
maruel@chromium.org1d9f6292011-04-07 14:15:36 +0000446 - The process will be killed after |timeout| seconds and returncode set to
447 TIMED_OUT.
szager@chromium.orge0558e62013-05-02 02:48:51 +0000448 - If the subprocess runs for |nag_timer| seconds without producing terminal
449 output, print a warning to stderr.
maruel@chromium.org421982f2011-04-01 17:38:06 +0000450 - Automatically passes stdin content as input so do not specify stdin=PIPE.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000451 """
452 stdin = kwargs.pop('stdin', None)
453 if stdin is not None:
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000454 if isinstance(stdin, basestring):
maruel@chromium.org0d5ef242011-04-18 13:52:58 +0000455 # When stdin is passed as an argument, use it as the actual input data and
456 # set the Popen() parameter accordingly.
457 kwargs['stdin'] = PIPE
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000458 else:
459 kwargs['stdin'] = stdin
460 stdin = None
maruel@chromium.org4860f052011-03-25 20:34:38 +0000461
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000462 proc = Popen(args, **kwargs)
maruel@chromium.org740a6c02011-12-05 23:46:44 +0000463 if stdin:
szager@chromium.orge0558e62013-05-02 02:48:51 +0000464 return proc.communicate(stdin, timeout, nag_timer), proc.returncode
maruel@chromium.org94c712f2011-12-01 15:04:57 +0000465 else:
szager@chromium.orge0558e62013-05-02 02:48:51 +0000466 return proc.communicate(None, timeout, nag_timer), proc.returncode
maruel@chromium.org4860f052011-03-25 20:34:38 +0000467
468
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000469def call(args, **kwargs):
470 """Emulates subprocess.call().
471
472 Automatically convert stdout=PIPE or stderr=PIPE to VOID.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000473 In no case they can be returned since no code path raises
474 subprocess2.CalledProcessError.
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000475 """
476 if kwargs.get('stdout') == PIPE:
477 kwargs['stdout'] = VOID
478 if kwargs.get('stderr') == PIPE:
479 kwargs['stderr'] = VOID
480 return communicate(args, **kwargs)[1]
481
482
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000483def check_call_out(args, **kwargs):
maruel@chromium.org421982f2011-04-01 17:38:06 +0000484 """Improved version of subprocess.check_call().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000485
maruel@chromium.org421982f2011-04-01 17:38:06 +0000486 Returns (stdout, stderr), unlike subprocess.check_call().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000487 """
maruel@chromium.org1f063db2011-04-18 19:04:52 +0000488 out, returncode = communicate(args, **kwargs)
maruel@chromium.org4860f052011-03-25 20:34:38 +0000489 if returncode:
490 raise CalledProcessError(
491 returncode, args, kwargs.get('cwd'), out[0], out[1])
492 return out
493
494
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000495def check_call(args, **kwargs):
496 """Emulate subprocess.check_call()."""
497 check_call_out(args, **kwargs)
498 return 0
499
500
maruel@chromium.org4860f052011-03-25 20:34:38 +0000501def capture(args, **kwargs):
502 """Captures stdout of a process call and returns it.
503
maruel@chromium.org421982f2011-04-01 17:38:06 +0000504 Returns stdout.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000505
maruel@chromium.org421982f2011-04-01 17:38:06 +0000506 - Discards returncode.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000507 - Blocks stdin by default if not specified since no output will be visible.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000508 """
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000509 kwargs.setdefault('stdin', VOID)
510
511 # Like check_output, deny the caller from using stdout arg.
512 return communicate(args, stdout=PIPE, **kwargs)[0][0]
maruel@chromium.org4860f052011-03-25 20:34:38 +0000513
514
515def check_output(args, **kwargs):
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000516 """Emulates subprocess.check_output().
maruel@chromium.org4860f052011-03-25 20:34:38 +0000517
maruel@chromium.org0bcd1d32011-04-26 15:55:49 +0000518 Captures stdout of a process call and returns stdout only.
maruel@chromium.org4860f052011-03-25 20:34:38 +0000519
maruel@chromium.org421982f2011-04-01 17:38:06 +0000520 - Throws if return code is not 0.
521 - Works even prior to python 2.7.
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000522 - Blocks stdin by default if not specified since no output will be visible.
523 - As per doc, "The stdout argument is not allowed as it is used internally."
maruel@chromium.org4860f052011-03-25 20:34:38 +0000524 """
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000525 kwargs.setdefault('stdin', VOID)
maruel@chromium.orgdb59bfc2011-11-30 14:03:14 +0000526 if 'stdout' in kwargs:
pgervais@chromium.org022d06e2014-04-29 17:08:12 +0000527 raise ValueError('stdout argument not allowed, it would be overridden.')
maruel@chromium.org87e6d332011-09-09 19:01:28 +0000528 return check_call_out(args, stdout=PIPE, **kwargs)[0]