| OLD | NEW |
| (Empty) |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import os | |
| 6 import re | |
| 7 | |
| 8 from third_party import colorama | |
| 9 | |
| 10 | |
| 11 UNITS = ('', 'k', 'm', 'g', 't', 'p', 'e', 'z', 'y') | |
| 12 | |
| 13 | |
| 14 def get_console_width(default=80): | |
| 15 """Returns the console width, if available.""" | |
| 16 # TODO(maruel): Implement Windows. | |
| 17 try: | |
| 18 _, columns = os.popen('stty size', 'r').read().split() | |
| 19 except (IOError, OSError, ValueError): | |
| 20 columns = default | |
| 21 return columns | |
| 22 | |
| 23 | |
| 24 def generate_histogram(data, buckets): | |
| 25 """Generates an histogram out of a list of floats. | |
| 26 | |
| 27 The data is bucketed into |buckets| buckets. | |
| 28 """ | |
| 29 if not data: | |
| 30 return {} | |
| 31 | |
| 32 minimum = min(data) | |
| 33 maximum = max(data) | |
| 34 if minimum == maximum: | |
| 35 return {data[0]: len(data)} | |
| 36 | |
| 37 buckets = min(len(data), buckets) | |
| 38 bucket_size = (maximum-minimum)/buckets | |
| 39 out = dict((i, 0) for i in xrange(buckets)) | |
| 40 for i in data: | |
| 41 out[min(int((i-minimum)/bucket_size), buckets-1)] += 1 | |
| 42 return dict(((k*bucket_size)+minimum, v) for k, v in out.iteritems()) | |
| 43 | |
| 44 | |
| 45 def print_histogram(data, columns, key_format): | |
| 46 """Prints ASCII art representing an histogram.""" | |
| 47 # TODO(maruel): Add dots for tens. | |
| 48 if not data: | |
| 49 # Nothing to print. | |
| 50 return | |
| 51 | |
| 52 max_key_width = max(len(key_format % k) for k in data) | |
| 53 # 3 == 1 for ' ' prefix, 2 for ': ' suffix. | |
| 54 width = columns - max_key_width - 3 | |
| 55 assert width > 1 | |
| 56 | |
| 57 maxvalue = max(data.itervalues()) | |
| 58 if all(isinstance(i, int) for i in data.itervalues()) and maxvalue < width: | |
| 59 width = maxvalue | |
| 60 norm = float(maxvalue) / width | |
| 61 | |
| 62 form = '%s: %s%s%s' | |
| 63 for k in sorted(data): | |
| 64 line = '*' * int(data[k] / norm) | |
| 65 key = (key_format % k).rjust(max_key_width) | |
| 66 print(form % (key, colorama.Fore.GREEN, line, colorama.Fore.RESET)) | |
| 67 | |
| 68 | |
| 69 def to_units(number): | |
| 70 """Convert a string to numbers.""" | |
| 71 unit = 0 | |
| 72 while number >= 1024.: | |
| 73 unit += 1 | |
| 74 number = number / 1024. | |
| 75 if unit == len(UNITS) - 1: | |
| 76 break | |
| 77 if unit: | |
| 78 return '%.2f%s' % (number, UNITS[unit]) | |
| 79 return '%d' % number | |
| 80 | |
| 81 | |
| 82 def from_units(text): | |
| 83 """Convert a text to numbers. | |
| 84 | |
| 85 Example: from_unit('0.1k') == 102 | |
| 86 """ | |
| 87 match = re.match(r'^([0-9\.]+)(|[' + ''.join(UNITS[1:]) + r'])$', text) | |
| 88 if not match: | |
| 89 return None | |
| 90 | |
| 91 number = float(match.group(1)) | |
| 92 unit = match.group(2) | |
| 93 return int(number * 1024**UNITS.index(unit)) | |
| 94 | |
| 95 | |
| 96 def unit_arg(option, opt, value, parser): | |
| 97 """OptionParser callback that supports units like 10.5m or 20k.""" | |
| 98 actual = from_units(value) | |
| 99 if actual is None: | |
| 100 parser.error('Invalid value \'%s\' for %s' % (value, opt)) | |
| 101 setattr(parser.values, option.dest, actual) | |
| 102 | |
| 103 | |
| 104 def unit_option(parser, *args, **kwargs): | |
| 105 """Add an option that uses unit_arg().""" | |
| 106 parser.add_option( | |
| 107 *args, type='str', metavar='N', action='callback', callback=unit_arg, | |
| 108 nargs=1, **kwargs) | |
| OLD | NEW |