Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(467)

Side by Side Diff: subprocess2.py

Issue 8808002: Using lists is faster than cStringIO. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/subprocess2_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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]
OLDNEW
« no previous file with comments | « no previous file | tests/subprocess2_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698