| OLD | NEW |
| 1 # Copyright 2009 Google Inc. All Rights Reserved. | 1 # Copyright 2009 Google Inc. All Rights Reserved. |
| 2 # | 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
| 6 # | 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 % (' '.join(args), kwargs.get('cwd', '.'))) | 278 % (' '.join(args), kwargs.get('cwd', '.'))) |
| 279 elif filter_fn: | 279 elif filter_fn: |
| 280 filter_fn(line) | 280 filter_fn(line) |
| 281 kwargs['filter_fn'] = filter_msg | 281 kwargs['filter_fn'] = filter_msg |
| 282 kwargs['call_filter_on_first_line'] = True | 282 kwargs['call_filter_on_first_line'] = True |
| 283 # Obviously. | 283 # Obviously. |
| 284 kwargs['print_stdout'] = True | 284 kwargs['print_stdout'] = True |
| 285 return CheckCallAndFilter(args, **kwargs) | 285 return CheckCallAndFilter(args, **kwargs) |
| 286 | 286 |
| 287 | 287 |
| 288 class StdoutAutoFlush(object): |
| 289 """Automatically flush after N seconds.""" |
| 290 def __init__(self, stdout, delay=10): |
| 291 self.lock = threading.Lock() |
| 292 self.stdout = stdout |
| 293 self.delay = delay |
| 294 self.last_flushed_at = time.time() |
| 295 self.stdout.flush() |
| 296 |
| 297 def write(self, out): |
| 298 """Thread-safe.""" |
| 299 self.stdout.write(out) |
| 300 should_flush = False |
| 301 with self.lock: |
| 302 if (time.time() - self.last_flushed_at) > self.delay: |
| 303 should_flush = True |
| 304 self.last_flushed_at = time.time() |
| 305 if should_flush: |
| 306 self.stdout.flush() |
| 307 |
| 308 def flush(self): |
| 309 self.stdout.flush() |
| 310 |
| 311 |
| 288 def CheckCallAndFilter(args, stdout=None, filter_fn=None, | 312 def CheckCallAndFilter(args, stdout=None, filter_fn=None, |
| 289 print_stdout=None, call_filter_on_first_line=False, | 313 print_stdout=None, call_filter_on_first_line=False, |
| 290 **kwargs): | 314 **kwargs): |
| 291 """Runs a command and calls back a filter function if needed. | 315 """Runs a command and calls back a filter function if needed. |
| 292 | 316 |
| 293 Accepts all subprocess.Popen() parameters plus: | 317 Accepts all subprocess.Popen() parameters plus: |
| 294 print_stdout: If True, the command's stdout is forwarded to stdout. | 318 print_stdout: If True, the command's stdout is forwarded to stdout. |
| 295 filter_fn: A function taking a single string argument called with each line | 319 filter_fn: A function taking a single string argument called with each line |
| 296 of the subprocess's output. Each line has the trailing newline | 320 of the subprocess's output. Each line has the trailing newline |
| 297 character trimmed. | 321 character trimmed. |
| 298 stdout: Can be any bufferable output. | 322 stdout: Can be any bufferable output. |
| 299 | 323 |
| 300 stderr is always redirected to stdout. | 324 stderr is always redirected to stdout. |
| 301 """ | 325 """ |
| 302 assert print_stdout or filter_fn | 326 assert print_stdout or filter_fn |
| 303 stdout = stdout or sys.stdout | 327 stdout = stdout or sys.stdout |
| 304 filter_fn = filter_fn or (lambda x: None) | 328 filter_fn = filter_fn or (lambda x: None) |
| 305 assert not 'stderr' in kwargs | 329 assert not 'stderr' in kwargs |
| 306 kid = Popen(args, bufsize=0, | 330 kid = Popen(args, bufsize=0, |
| 307 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, | 331 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
| 308 **kwargs) | 332 **kwargs) |
| 309 | 333 |
| 310 # Do a flush of stdout before we begin reading from the subprocess's stdout | 334 # Do a flush of stdout before we begin reading from the subprocess's stdout |
| 311 last_flushed_at = time.time() | |
| 312 stdout.flush() | 335 stdout.flush() |
| 313 | 336 |
| 314 # Also, we need to forward stdout to prevent weird re-ordering of output. | 337 # Also, we need to forward stdout to prevent weird re-ordering of output. |
| 315 # This has to be done on a per byte basis to make sure it is not buffered: | 338 # This has to be done on a per byte basis to make sure it is not buffered: |
| 316 # normally buffering is done for each line, but if svn requests input, no | 339 # normally buffering is done for each line, but if svn requests input, no |
| 317 # end-of-line character is output after the prompt and it would not show up. | 340 # end-of-line character is output after the prompt and it would not show up. |
| 318 in_byte = kid.stdout.read(1) | 341 in_byte = kid.stdout.read(1) |
| 319 if in_byte: | 342 if in_byte: |
| 320 if call_filter_on_first_line: | 343 if call_filter_on_first_line: |
| 321 filter_fn(None) | 344 filter_fn(None) |
| 322 in_line = '' | 345 in_line = '' |
| 323 while in_byte: | 346 while in_byte: |
| 324 if in_byte != '\r': | 347 if in_byte != '\r': |
| 325 if print_stdout: | 348 if print_stdout: |
| 326 stdout.write(in_byte) | 349 stdout.write(in_byte) |
| 327 if in_byte != '\n': | 350 if in_byte != '\n': |
| 328 in_line += in_byte | 351 in_line += in_byte |
| 329 else: | 352 else: |
| 330 filter_fn(in_line) | 353 filter_fn(in_line) |
| 331 in_line = '' | 354 in_line = '' |
| 332 # Flush at least 10 seconds between line writes. We wait at least 10 | |
| 333 # seconds to avoid overloading the reader that called us with output, | |
| 334 # which can slow busy readers down. | |
| 335 if (time.time() - last_flushed_at) > 10: | |
| 336 last_flushed_at = time.time() | |
| 337 stdout.flush() | |
| 338 in_byte = kid.stdout.read(1) | 355 in_byte = kid.stdout.read(1) |
| 339 # Flush the rest of buffered output. This is only an issue with | 356 # Flush the rest of buffered output. This is only an issue with |
| 340 # stdout/stderr not ending with a \n. | 357 # stdout/stderr not ending with a \n. |
| 341 if len(in_line): | 358 if len(in_line): |
| 342 filter_fn(in_line) | 359 filter_fn(in_line) |
| 343 rv = kid.wait() | 360 rv = kid.wait() |
| 344 if rv: | 361 if rv: |
| 345 raise CheckCallError(args, kwargs.get('cwd', None), rv, None) | 362 raise CheckCallError(args, kwargs.get('cwd', None), rv, None) |
| 346 return 0 | 363 return 0 |
| 347 | 364 |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 if exception: | 581 if exception: |
| 565 self.parent.exceptions.append(exception) | 582 self.parent.exceptions.append(exception) |
| 566 if self.parent.progress: | 583 if self.parent.progress: |
| 567 self.parent.progress.update(1) | 584 self.parent.progress.update(1) |
| 568 assert not self.item.name in self.parent.ran | 585 assert not self.item.name in self.parent.ran |
| 569 if not self.item.name in self.parent.ran: | 586 if not self.item.name in self.parent.ran: |
| 570 self.parent.ran.append(self.item.name) | 587 self.parent.ran.append(self.item.name) |
| 571 finally: | 588 finally: |
| 572 self.parent.ready_cond.notifyAll() | 589 self.parent.ready_cond.notifyAll() |
| 573 self.parent.ready_cond.release() | 590 self.parent.ready_cond.release() |
| OLD | NEW |