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 |