2 # Copyright (c) 2012 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.
7 In theory you shouldn't need anything else in subprocess, or this module failed.
21 # Constants forwarded from subprocess.
22 PIPE = subprocess.PIPE
23 STDOUT = subprocess.STDOUT
24 # Sends stdout or stderr to os.devnull.
26 # Error code when a process was killed because it timed out.
30 # Set to True if you somehow need to disable this hack.
31 SUBPROCESS_CLEANUP_HACKED = False
34 class 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, output=stdout)
38 self.stdout = self.output # for backward compatibility.
43 out = 'Command %s returned non-zero exit status %s' % (
44 ' '.join(self.cmd), self.returncode)
46 out += ' in ' + self.cwd
47 return '\n'.join(filter(None, (out, self.stdout, self.stderr)))
50 class CygwinRebaseError(CalledProcessError):
51 """Occurs when cygwin's fork() emulation fails due to rebased dll."""
58 """Kills a process by its process id."""
60 # Unable to import 'module'
61 # pylint: disable=E1101,F0401
63 return os.kill(pid, signal.SIGKILL)
68 def kill_win(process):
69 """Kills a process with its windows handle.
71 Has no effect on other platforms.
74 # Unable to import 'module'
75 # pylint: disable=F0401
77 # Access to a protected member _handle of a client class
78 # pylint: disable=W0212
79 return win32process.TerminateProcess(process._handle, -1)
85 """Adds kill() method to subprocess.Popen for python <2.6"""
86 if hasattr(subprocess.Popen, 'kill'):
89 if sys.platform == 'win32':
90 subprocess.Popen.kill = kill_win
92 subprocess.Popen.kill = lambda process: kill_pid(process.pid)
95 def hack_subprocess():
96 """subprocess functions may throw exceptions when used in multiple threads.
98 See http://bugs.python.org/issue1731717 for more information.
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
108 def get_english_env(env):
109 """Forces LANG and/or LANGUAGE to be English.
111 Forces encoding to utf-8 for subprocesses.
113 Returns None if it is unnecessary.
115 if sys.platform == 'win32':
117 env = env or os.environ
119 # Test if it is necessary at all.
120 is_english = lambda name: env.get(name, 'en').startswith('en')
122 if is_english('LANG') and is_english('LANGUAGE'):
125 # Requires modifications.
128 if not is_english(name):
129 env[name] = 'en_US.UTF-8'
135 class NagTimer(object):
137 Triggers a callback when a time interval passes without an event being fired.
139 For example, the event could be receiving terminal output from a subprocess;
140 and the callback could print a warning to stderr that the subprocess appeared
143 def __init__(self, interval, cb):
144 self.interval = interval
146 self.timer = threading.Timer(self.interval, self.fn)
147 self.last_output = self.previous_last_output = 0
150 self.last_output = self.previous_last_output = time.time()
154 self.last_output = time.time()
158 if self.last_output == self.previous_last_output:
159 self.cb(now - self.previous_last_output)
160 # Use 0.1 fudge factor, just in case
161 # (self.last_output - now) is very close to zero.
162 sleep_time = (self.last_output - now - 0.1) % self.interval
163 self.previous_last_output = self.last_output
164 self.timer = threading.Timer(sleep_time + 0.1, self.fn)
171 class Popen(subprocess.Popen):
172 """Wraps subprocess.Popen() with various workarounds.
174 - Forces English output since it's easier to parse the stdout if it is always
176 - Sets shell=True on windows by default. You can override this by forcing
177 shell parameter to a value.
178 - Adds support for VOID to not buffer when not needed.
179 - Adds self.start property.
181 Note: Popen() can throw OSError when cwd or args[0] doesn't exist. Translate
182 exceptions generated by cygwin when it fails trying to emulate fork().
184 def __init__(self, args, **kwargs):
185 # Make sure we hack subprocess if necessary.
189 env = get_english_env(kwargs.get('env'))
192 if kwargs.get('shell') is None:
193 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
194 # the executable, but shell=True makes subprocess on Linux fail when it's
195 # called with a list because it only tries to execute the first item in
197 kwargs['shell'] = bool(sys.platform=='win32')
199 if isinstance(args, basestring):
201 elif isinstance(args, (list, tuple)):
202 tmp_str = ' '.join(args)
204 raise CalledProcessError(None, args, kwargs.get('cwd'), None, None)
205 if kwargs.get('cwd', None):
206 tmp_str += '; cwd=%s' % kwargs['cwd']
207 logging.debug(tmp_str)
209 self.stdout_cb = None
210 self.stderr_cb = None
211 self.stdin_is_void = False
212 self.stdout_is_void = False
213 self.stderr_is_void = False
214 self.cmd_str = tmp_str
216 if kwargs.get('stdin') is VOID:
217 kwargs['stdin'] = open(os.devnull, 'r')
218 self.stdin_is_void = True
220 for stream in ('stdout', 'stderr'):
221 if kwargs.get(stream) in (VOID, os.devnull):
222 kwargs[stream] = open(os.devnull, 'w')
223 setattr(self, stream + '_is_void', True)
224 if callable(kwargs.get(stream)):
225 setattr(self, stream + '_cb', kwargs[stream])
226 kwargs[stream] = PIPE
228 self.start = time.time()
230 self.nag_timer = None
232 self.shell = kwargs.get('shell', None)
233 # Silence pylint on MacOSX
234 self.returncode = None
237 super(Popen, self).__init__(args, **kwargs)
239 if e.errno == errno.EAGAIN and sys.platform == 'cygwin':
240 # Convert fork() emulation failure into a CygwinRebaseError().
241 raise CygwinRebaseError(
247 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure '
248 'to learn how to fix this error; you need to rebase your cygwin '
250 # Popen() can throw OSError when cwd or args[0] doesn't exist.
251 raise OSError('Execution failed with error: %s.\n'
252 'Check that %s or %s exist and have execution permission.'
253 % (str(e), kwargs.get('cwd'), args[0]))
255 def _tee_threads(self, input): # pylint: disable=W0622
256 """Does I/O for a process's pipes using threads.
258 It's the simplest and slowest implementation. Expect very slow behavior.
260 If there is a callback and it doesn't keep up with the calls, the timeout
261 effectiveness will be delayed accordingly.
263 # Queue of either of <threadname> when done or (<threadname>, data). In
264 # theory we would like to limit to ~64kb items to not cause large memory
265 # usage when the callback blocks. It is not done because it slows down
266 # processing on OSX10.6 by a factor of 2x, making it even slower than
267 # Windows! Revisit this decision if it becomes a problem, e.g. crash
268 # because of memory exhaustion.
269 queue = Queue.Queue()
270 done = threading.Event()
275 stdin_io = cStringIO.StringIO(input)
277 data = stdin_io.read(1024)
279 self.stdin.write(data)
286 def _queue_pipe_read(pipe, name):
287 """Queues characters read from a pipe into a queue."""
295 queue.put((name, data))
301 done.wait(self.timeout)
311 # Starts up to 5 threads:
312 # Wait for the process to quit
318 'wait': threading.Thread(target=wait_fn),
320 if self.timeout is not None:
321 threads['timeout'] = threading.Thread(target=timeout_fn)
323 threads['stdout'] = threading.Thread(
324 target=_queue_pipe_read, args=(self.stdout, 'stdout'))
326 threads['stderr'] = threading.Thread(
327 target=_queue_pipe_read, args=(self.stderr, 'stderr'))
329 threads['stdin'] = threading.Thread(target=write_stdin)
331 # Pipe but no input, make sure it's closed.
333 for t in threads.itervalues():
337 def _nag_cb(elapsed):
338 logging.warn(' No output for %.0f seconds from command:' % elapsed)
339 logging.warn(' %s' % self.cmd_str)
341 int('%.0f' % (elapsed / self.nag_timer)) >= self.nag_max):
343 done.set() # Must do this so that timeout thread stops waiting.
344 nag = NagTimer(self.nag_timer, _nag_cb)
349 # This thread needs to be optimized for speed.
352 if item[0] == 'stdout':
353 self.stdout_cb(item[1])
354 elif item[0] == 'stderr':
355 self.stderr_cb(item[1])
357 # A thread terminated.
362 # Terminate the timeout thread if necessary.
364 elif item == 'timeout' and not timed_out and self.poll() is None:
365 logging.debug('Timed out after %.0fs: killing' % (
366 time.time() - self.start))
374 if 'wait' in threads:
375 # Accelerate things, otherwise it would hang until the child process is
377 logging.debug('Killing child because of an exception')
380 for thread in threads.itervalues():
383 self.returncode = TIMED_OUT
385 # pylint: disable=W0221,W0622
386 def communicate(self, input=None, timeout=None, nag_timer=None,
388 """Adds timeout and callbacks support.
390 Returns (stdout, stderr) like subprocess.Popen().communicate().
392 - The process will be killed after |timeout| seconds and returncode set to
394 - If the subprocess runs for |nag_timer| seconds without producing terminal
395 output, print a warning to stderr.
397 self.timeout = timeout
398 self.nag_timer = nag_timer
399 self.nag_max = nag_max
400 if (not self.timeout and not self.nag_timer and
401 not self.stdout_cb and not self.stderr_cb):
402 return super(Popen, self).communicate(input)
404 if self.timeout and self.shell:
406 'Using timeout and shell simultaneously will cause a process leak '
407 'since the shell will be killed instead of the child process.')
411 # Convert to a lambda to workaround python's deadlock.
412 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
413 # When the pipe fills up, it would deadlock this process.
414 if self.stdout and not self.stdout_cb and not self.stdout_is_void:
416 self.stdout_cb = stdout.append
417 if self.stderr and not self.stderr_cb and not self.stderr_is_void:
419 self.stderr_cb = stderr.append
420 self._tee_threads(input)
421 if stdout is not None:
422 stdout = ''.join(stdout)
423 if stderr is not None:
424 stderr = ''.join(stderr)
425 return (stdout, stderr)
428 def communicate(args, timeout=None, nag_timer=None, nag_max=None, **kwargs):
429 """Wraps subprocess.Popen().communicate() and add timeout support.
431 Returns ((stdout, stderr), returncode).
433 - The process will be killed after |timeout| seconds and returncode set to
435 - If the subprocess runs for |nag_timer| seconds without producing terminal
436 output, print a warning to stderr.
437 - Automatically passes stdin content as input so do not specify stdin=PIPE.
439 stdin = kwargs.pop('stdin', None)
440 if stdin is not None:
441 if isinstance(stdin, basestring):
442 # When stdin is passed as an argument, use it as the actual input data and
443 # set the Popen() parameter accordingly.
444 kwargs['stdin'] = PIPE
446 kwargs['stdin'] = stdin
449 proc = Popen(args, **kwargs)
451 return proc.communicate(stdin, timeout, nag_timer), proc.returncode
453 return proc.communicate(None, timeout, nag_timer), proc.returncode
456 def call(args, **kwargs):
457 """Emulates subprocess.call().
459 Automatically convert stdout=PIPE or stderr=PIPE to VOID.
460 In no case they can be returned since no code path raises
461 subprocess2.CalledProcessError.
463 if kwargs.get('stdout') == PIPE:
464 kwargs['stdout'] = VOID
465 if kwargs.get('stderr') == PIPE:
466 kwargs['stderr'] = VOID
467 return communicate(args, **kwargs)[1]
470 def check_call_out(args, **kwargs):
471 """Improved version of subprocess.check_call().
473 Returns (stdout, stderr), unlike subprocess.check_call().
475 out, returncode = communicate(args, **kwargs)
477 raise CalledProcessError(
478 returncode, args, kwargs.get('cwd'), out[0], out[1])
482 def check_call(args, **kwargs):
483 """Emulate subprocess.check_call()."""
484 check_call_out(args, **kwargs)
488 def capture(args, **kwargs):
489 """Captures stdout of a process call and returns it.
493 - Discards returncode.
494 - Blocks stdin by default if not specified since no output will be visible.
496 kwargs.setdefault('stdin', VOID)
498 # Like check_output, deny the caller from using stdout arg.
499 return communicate(args, stdout=PIPE, **kwargs)[0][0]
502 def check_output(args, **kwargs):
503 """Emulates subprocess.check_output().
505 Captures stdout of a process call and returns stdout only.
507 - Throws if return code is not 0.
508 - Works even prior to python 2.7.
509 - Blocks stdin by default if not specified since no output will be visible.
510 - As per doc, "The stdout argument is not allowed as it is used internally."
512 kwargs.setdefault('stdin', VOID)
513 if 'stdout' in kwargs:
514 raise ValueError('stdout argument not allowed, it would be overridden.')
515 return check_call_out(args, stdout=PIPE, **kwargs)[0]