| OLD | NEW |
| 1 # coding=utf8 | 1 # coding=utf8 |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Collection of subprocess wrapper functions. | 5 """Collection of subprocess wrapper functions. |
| 6 | 6 |
| 7 In theory you shouldn't need anything else in subprocess, or this module failed. | 7 In theory you shouldn't need anything else in subprocess, or this module failed. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 from __future__ import with_statement | 10 from __future__ import with_statement |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 elif isinstance(args, (list, tuple)): | 166 elif isinstance(args, (list, tuple)): |
| 167 tmp_str = ' '.join(args) | 167 tmp_str = ' '.join(args) |
| 168 else: | 168 else: |
| 169 raise CalledProcessError(None, args, kwargs.get('cwd'), None, None) | 169 raise CalledProcessError(None, args, kwargs.get('cwd'), None, None) |
| 170 if kwargs.get('cwd', None): | 170 if kwargs.get('cwd', None): |
| 171 tmp_str += '; cwd=%s' % kwargs['cwd'] | 171 tmp_str += '; cwd=%s' % kwargs['cwd'] |
| 172 logging.debug(tmp_str) | 172 logging.debug(tmp_str) |
| 173 | 173 |
| 174 self.stdout_cb = None | 174 self.stdout_cb = None |
| 175 self.stderr_cb = None | 175 self.stderr_cb = None |
| 176 self.stdout_void = False | 176 self.stdin_is_void = False |
| 177 self.stderr_void = False | 177 self.stdout_is_void = False |
| 178 def fix(stream): | 178 self.stderr_is_void = False |
| 179 |
| 180 if kwargs.get('stdin') is VOID: |
| 181 kwargs['stdin'] = open(os.devnull, 'r') |
| 182 self.stdin_is_void = True |
| 183 |
| 184 for stream in ('stdout', 'stderr'): |
| 179 if kwargs.get(stream) in (VOID, os.devnull): | 185 if kwargs.get(stream) in (VOID, os.devnull): |
| 180 # Replaces VOID with handle to /dev/null. | |
| 181 # Create a temporary file to workaround python's deadlock. | |
| 182 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait | |
| 183 # When the pipe fills up, it will deadlock this process. Using a real | |
| 184 # file works around that issue. | |
| 185 kwargs[stream] = open(os.devnull, 'w') | 186 kwargs[stream] = open(os.devnull, 'w') |
| 186 setattr(self, stream + '_void', True) | 187 setattr(self, stream + '_is_void', True) |
| 187 if callable(kwargs.get(stream)): | 188 if callable(kwargs.get(stream)): |
| 188 # Callable stdout/stderr should be used only with call() wrappers. | |
| 189 setattr(self, stream + '_cb', kwargs[stream]) | 189 setattr(self, stream + '_cb', kwargs[stream]) |
| 190 kwargs[stream] = PIPE | 190 kwargs[stream] = PIPE |
| 191 | 191 |
| 192 fix('stdout') | |
| 193 fix('stderr') | |
| 194 | |
| 195 self.start = time.time() | 192 self.start = time.time() |
| 196 self.timeout = None | 193 self.timeout = None |
| 197 self.shell = kwargs.get('shell', None) | 194 self.shell = kwargs.get('shell', None) |
| 198 # Silence pylint on MacOSX | 195 # Silence pylint on MacOSX |
| 199 self.returncode = None | 196 self.returncode = None |
| 200 | 197 |
| 201 try: | 198 try: |
| 202 super(Popen, self).__init__(args, **kwargs) | 199 super(Popen, self).__init__(args, **kwargs) |
| 203 except OSError, e: | 200 except OSError, e: |
| 204 if e.errno == errno.EAGAIN and sys.platform == 'cygwin': | 201 if e.errno == errno.EAGAIN and sys.platform == 'cygwin': |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 if self.timeout is not None: | 278 if self.timeout is not None: |
| 282 threads['timeout'] = threading.Thread(target=timeout_fn) | 279 threads['timeout'] = threading.Thread(target=timeout_fn) |
| 283 if self.stdout_cb: | 280 if self.stdout_cb: |
| 284 threads['stdout'] = threading.Thread( | 281 threads['stdout'] = threading.Thread( |
| 285 target=_queue_pipe_read, args=(self.stdout, 'stdout')) | 282 target=_queue_pipe_read, args=(self.stdout, 'stdout')) |
| 286 if self.stderr_cb: | 283 if self.stderr_cb: |
| 287 threads['stderr'] = threading.Thread( | 284 threads['stderr'] = threading.Thread( |
| 288 target=_queue_pipe_read, args=(self.stderr, 'stderr')) | 285 target=_queue_pipe_read, args=(self.stderr, 'stderr')) |
| 289 if input: | 286 if input: |
| 290 threads['stdin'] = threading.Thread(target=write_stdin) | 287 threads['stdin'] = threading.Thread(target=write_stdin) |
| 288 elif self.stdin: |
| 289 # Pipe but no input, make sure it's closed. |
| 290 self.stdin.close() |
| 291 for t in threads.itervalues(): | 291 for t in threads.itervalues(): |
| 292 t.start() | 292 t.start() |
| 293 | 293 |
| 294 timed_out = False | 294 timed_out = False |
| 295 try: | 295 try: |
| 296 # This thread needs to be optimized for speed. | 296 # This thread needs to be optimized for speed. |
| 297 while threads: | 297 while threads: |
| 298 item = queue.get() | 298 item = queue.get() |
| 299 if item[0] is 'stdout': | 299 if item[0] is 'stdout': |
| 300 self.stdout_cb(item[1]) | 300 self.stdout_cb(item[1]) |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 | 339 |
| 340 if self.timeout and self.shell: | 340 if self.timeout and self.shell: |
| 341 raise TypeError( | 341 raise TypeError( |
| 342 'Using timeout and shell simultaneously will cause a process leak ' | 342 'Using timeout and shell simultaneously will cause a process leak ' |
| 343 'since the shell will be killed instead of the child process.') | 343 'since the shell will be killed instead of the child process.') |
| 344 | 344 |
| 345 stdout = None | 345 stdout = None |
| 346 stderr = None | 346 stderr = None |
| 347 # Convert to a lambda to workaround python's deadlock. | 347 # Convert to a lambda to workaround python's deadlock. |
| 348 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait | 348 # http://docs.python.org/library/subprocess.html#subprocess.Popen.wait |
| 349 # When the pipe fills up, it will deadlock this process. Using a thread | 349 # When the pipe fills up, it would deadlock this process. |
| 350 # works around that issue. No need for thread safe function since the call | 350 if self.stdout and not self.stdout_cb and not self.stdout_is_void: |
| 351 # backs are guaranteed to be called from the main thread. | 351 stdout = [] |
| 352 if self.stdout and not self.stdout_cb and not self.stdout_void: | 352 self.stdout_cb = stdout.append |
| 353 stdout = cStringIO.StringIO() | 353 if self.stderr and not self.stderr_cb and not self.stderr_is_void: |
| 354 self.stdout_cb = stdout.write | 354 stderr = [] |
| 355 if self.stderr and not self.stderr_cb and not self.stderr_void: | 355 self.stderr_cb = stderr.append |
| 356 stderr = cStringIO.StringIO() | |
| 357 self.stderr_cb = stderr.write | |
| 358 self._tee_threads(input) | 356 self._tee_threads(input) |
| 359 if stdout: | 357 if stdout is not None: |
| 360 stdout = stdout.getvalue() | 358 stdout = ''.join(stdout) |
| 361 if stderr: | 359 stderr = None |
| 362 stderr = stderr.getvalue() | 360 if stderr is not None: |
| 361 stderr = ''.join(stderr) |
| 363 return (stdout, stderr) | 362 return (stdout, stderr) |
| 364 | 363 |
| 365 | 364 |
| 366 def communicate(args, timeout=None, **kwargs): | 365 def communicate(args, timeout=None, **kwargs): |
| 367 """Wraps subprocess.Popen().communicate() and add timeout support. | 366 """Wraps subprocess.Popen().communicate() and add timeout support. |
| 368 | 367 |
| 369 Returns ((stdout, stderr), returncode). | 368 Returns ((stdout, stderr), returncode). |
| 370 | 369 |
| 371 - The process will be killed after |timeout| seconds and returncode set to | 370 - The process will be killed after |timeout| seconds and returncode set to |
| 372 TIMED_OUT. | 371 TIMED_OUT. |
| 373 - Automatically passes stdin content as input so do not specify stdin=PIPE. | 372 - Automatically passes stdin content as input so do not specify stdin=PIPE. |
| 374 """ | 373 """ |
| 375 stdin = kwargs.pop('stdin', None) | 374 stdin = kwargs.pop('stdin', None) |
| 376 if stdin is not None: | 375 if stdin is not None: |
| 377 if stdin is VOID: | 376 if isinstance(stdin, basestring): |
| 378 kwargs['stdin'] = open(os.devnull, 'r') | |
| 379 stdin = None | |
| 380 else: | |
| 381 assert isinstance(stdin, basestring) | |
| 382 # When stdin is passed as an argument, use it as the actual input data and | 377 # When stdin is passed as an argument, use it as the actual input data and |
| 383 # set the Popen() parameter accordingly. | 378 # set the Popen() parameter accordingly. |
| 384 kwargs['stdin'] = PIPE | 379 kwargs['stdin'] = PIPE |
| 380 else: |
| 381 kwargs['stdin'] = stdin |
| 382 stdin = None |
| 385 | 383 |
| 386 proc = Popen(args, **kwargs) | 384 proc = Popen(args, **kwargs) |
| 387 if stdin not in (None, VOID): | 385 if stdin: |
| 388 return proc.communicate(stdin, timeout), proc.returncode | 386 return proc.communicate(stdin, timeout), proc.returncode |
| 389 else: | 387 else: |
| 390 return proc.communicate(None, timeout), proc.returncode | 388 return proc.communicate(None, timeout), proc.returncode |
| 391 | 389 |
| 392 | 390 |
| 393 def call(args, **kwargs): | 391 def call(args, **kwargs): |
| 394 """Emulates subprocess.call(). | 392 """Emulates subprocess.call(). |
| 395 | 393 |
| 396 Automatically convert stdout=PIPE or stderr=PIPE to VOID. | 394 Automatically convert stdout=PIPE or stderr=PIPE to VOID. |
| 397 In no case they can be returned since no code path raises | 395 In no case they can be returned since no code path raises |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 443 | 441 |
| 444 - Throws if return code is not 0. | 442 - Throws if return code is not 0. |
| 445 - Works even prior to python 2.7. | 443 - Works even prior to python 2.7. |
| 446 - Blocks stdin by default if not specified since no output will be visible. | 444 - Blocks stdin by default if not specified since no output will be visible. |
| 447 - As per doc, "The stdout argument is not allowed as it is used internally." | 445 - As per doc, "The stdout argument is not allowed as it is used internally." |
| 448 """ | 446 """ |
| 449 kwargs.setdefault('stdin', VOID) | 447 kwargs.setdefault('stdin', VOID) |
| 450 if 'stdout' in kwargs: | 448 if 'stdout' in kwargs: |
| 451 raise ValueError('stdout argument not allowed, it will be overridden.') | 449 raise ValueError('stdout argument not allowed, it will be overridden.') |
| 452 return check_call_out(args, stdout=PIPE, **kwargs)[0] | 450 return check_call_out(args, stdout=PIPE, **kwargs)[0] |
| OLD | NEW |