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 |