| OLD | NEW |
| 1 # Copyright (C) 2010, 2012 Google Inc. All rights reserved. | 1 # Copyright (C) 2010, 2012 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 | 28 |
| 29 import logging | 29 import logging |
| 30 import os | 30 import os |
| 31 import sys | 31 import sys |
| 32 import time | 32 import time |
| 33 | 33 |
| 34 LOG_HANDLER_NAME = 'MeteredStreamLogHandler' | 34 LOG_HANDLER_NAME = 'MeteredStreamLogHandler' |
| 35 | 35 |
| 36 | 36 |
| 37 class MeteredStream(object): | 37 class MeteredStream(object): |
| 38 |
| 38 """ | 39 """ |
| 39 This class implements a stream wrapper that has 'meters' as well as | 40 This class implements a stream wrapper that has 'meters' as well as |
| 40 regular output. A 'meter' is a single line of text that can be erased | 41 regular output. A 'meter' is a single line of text that can be erased |
| 41 and rewritten repeatedly, without producing multiple lines of output. It | 42 and rewritten repeatedly, without producing multiple lines of output. It |
| 42 can be used to produce effects like progress bars. | 43 can be used to produce effects like progress bars. |
| 43 """ | 44 """ |
| 44 | 45 |
| 45 @staticmethod | 46 @staticmethod |
| 46 def _erasure(txt): | 47 def _erasure(txt): |
| 47 num_chars = len(txt) | 48 num_chars = len(txt) |
| 48 return '\b' * num_chars + ' ' * num_chars + '\b' * num_chars | 49 return '\b' * num_chars + ' ' * num_chars + '\b' * num_chars |
| 49 | 50 |
| 50 @staticmethod | 51 @staticmethod |
| 51 def _ensure_newline(txt): | 52 def _ensure_newline(txt): |
| 52 return txt if txt.endswith('\n') else txt + '\n' | 53 return txt if txt.endswith('\n') else txt + '\n' |
| 53 | 54 |
| 54 def __init__(self, stream=None, verbose=False, logger=None, time_fn=None, pi
d=None, number_of_columns=None): | 55 def __init__(self, stream=None, verbose=False, logger=None, time_fn=None, pi
d=None, number_of_columns=None): |
| 55 self._stream = stream or sys.stderr | 56 self._stream = stream or sys.stderr |
| 56 self._verbose = verbose | 57 self._verbose = verbose |
| 57 self._time_fn = time_fn or time.time | 58 self._time_fn = time_fn or time.time |
| 58 self._pid = pid or os.getpid() | 59 self._pid = pid or os.getpid() |
| 59 self._isatty = self._stream.isatty() | 60 self._isatty = self._stream.isatty() |
| 60 self._erasing = self._isatty and not verbose | 61 self._erasing = self._isatty and not verbose |
| 61 self._last_partial_line = '' | 62 self._last_partial_line = '' |
| 62 self._last_write_time = 0.0 | 63 self._last_write_time = 0.0 |
| 63 self._throttle_delay_in_secs = 0.066 if self._erasing else 10.0 | 64 self._throttle_delay_in_secs = 0.066 if self._erasing else 10.0 |
| 64 self._number_of_columns = sys.maxint | 65 self._number_of_columns = sys.maxsize |
| 65 if self._isatty and number_of_columns: | 66 if self._isatty and number_of_columns: |
| 66 self._number_of_columns = number_of_columns | 67 self._number_of_columns = number_of_columns |
| 67 | 68 |
| 68 self._logger = logger | 69 self._logger = logger |
| 69 self._log_handler = None | 70 self._log_handler = None |
| 70 if self._logger: | 71 if self._logger: |
| 71 log_level = logging.DEBUG if verbose else logging.INFO | 72 log_level = logging.DEBUG if verbose else logging.INFO |
| 72 self._log_handler = _LogHandler(self) | 73 self._log_handler = _LogHandler(self) |
| 73 self._log_handler.setLevel(log_level) | 74 self._log_handler.setLevel(log_level) |
| 74 self._logger.addHandler(self._log_handler) | 75 self._logger.addHandler(self._log_handler) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 92 self._last_partial_line = txt[txt.rfind('\n') + 1:] | 93 self._last_partial_line = txt[txt.rfind('\n') + 1:] |
| 93 | 94 |
| 94 def write(self, txt, now=None, pid=None): | 95 def write(self, txt, now=None, pid=None): |
| 95 now = now or self._time_fn() | 96 now = now or self._time_fn() |
| 96 pid = pid or self._pid | 97 pid = pid or self._pid |
| 97 self._last_write_time = now | 98 self._last_write_time = now |
| 98 if self._last_partial_line: | 99 if self._last_partial_line: |
| 99 self._erase_last_partial_line() | 100 self._erase_last_partial_line() |
| 100 if self._verbose: | 101 if self._verbose: |
| 101 now_tuple = time.localtime(now) | 102 now_tuple = time.localtime(now) |
| 102 msg = '%02d:%02d:%02d.%03d %d %s' % (now_tuple.tm_hour, now_tuple.tm
_min, now_tuple.tm_sec, int((now * 1000) % 1000), pid, self._ensure_newline(txt)
) | 103 msg = '%02d:%02d:%02d.%03d %d %s' % (now_tuple.tm_hour, |
| 104 now_tuple.tm_min, |
| 105 now_tuple.tm_sec, |
| 106 int((now * 1000) % 1000), |
| 107 pid, |
| 108 self._ensure_newline(txt)) |
| 103 elif self._isatty: | 109 elif self._isatty: |
| 104 msg = txt | 110 msg = txt |
| 105 else: | 111 else: |
| 106 msg = self._ensure_newline(txt) | 112 msg = self._ensure_newline(txt) |
| 107 | 113 |
| 108 self._stream.write(msg) | 114 self._stream.write(msg) |
| 109 | 115 |
| 110 def writeln(self, txt, now=None, pid=None): | 116 def writeln(self, txt, now=None, pid=None): |
| 111 self.write(self._ensure_newline(txt), now, pid) | 117 self.write(self._ensure_newline(txt), now, pid) |
| 112 | 118 |
| 113 def _erase_last_partial_line(self): | 119 def _erase_last_partial_line(self): |
| 114 num_chars = len(self._last_partial_line) | 120 num_chars = len(self._last_partial_line) |
| 115 self._stream.write(self._erasure(self._last_partial_line)) | 121 self._stream.write(self._erasure(self._last_partial_line)) |
| 116 self._last_partial_line = '' | 122 self._last_partial_line = '' |
| 117 | 123 |
| 118 def flush(self): | 124 def flush(self): |
| 119 if self._last_partial_line: | 125 if self._last_partial_line: |
| 120 self._stream.write('\n') | 126 self._stream.write('\n') |
| 121 self._last_partial_line = '' | 127 self._last_partial_line = '' |
| 122 self._stream.flush() | 128 self._stream.flush() |
| 123 | 129 |
| 124 def number_of_columns(self): | 130 def number_of_columns(self): |
| 125 return self._number_of_columns | 131 return self._number_of_columns |
| 126 | 132 |
| 127 | 133 |
| 128 class _LogHandler(logging.Handler): | 134 class _LogHandler(logging.Handler): |
| 135 |
| 129 def __init__(self, meter): | 136 def __init__(self, meter): |
| 130 logging.Handler.__init__(self) | 137 logging.Handler.__init__(self) |
| 131 self._meter = meter | 138 self._meter = meter |
| 132 self.name = LOG_HANDLER_NAME | 139 self.name = LOG_HANDLER_NAME |
| 133 | 140 |
| 134 def emit(self, record): | 141 def emit(self, record): |
| 135 self._meter.writeln(record.getMessage(), record.created, record.process) | 142 self._meter.writeln(record.getMessage(), record.created, record.process) |
| OLD | NEW |