js/src/tests/lib/progressbar.py

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:27d40ba10071
1 # Text progress bar library, like curl or scp.
2
3 from datetime import datetime, timedelta
4 import math
5 import sys
6
7 if sys.platform.startswith('win'):
8 from terminal_win import Terminal
9 else:
10 from terminal_unix import Terminal
11
12 class NullProgressBar(object):
13 def update(self, current, data): pass
14 def poke(self): pass
15 def finish(self, complete=True): pass
16 def beginline(self): pass
17 def message(self, msg): sys.stdout.write(msg + '\n')
18 def update_granularity(self): return timedelta.max
19
20 class ProgressBar(object):
21 def __init__(self, limit, fmt):
22 assert self.conservative_isatty()
23
24 self.prior = None
25 self.atLineStart = True
26 self.counters_fmt = fmt # [{str:str}] Describtion of how to lay out each
27 # field in the counters map.
28 self.limit = limit # int: The value of 'current' equal to 100%.
29 self.limit_digits = int(math.ceil(math.log10(self.limit))) # int: max digits in limit
30 self.t0 = datetime.now() # datetime: The start time.
31
32 # Compute the width of the counters and build the format string.
33 self.counters_width = 1 # [
34 for layout in self.counters_fmt:
35 self.counters_width += self.limit_digits
36 self.counters_width += 1 # | (or ']' for the last one)
37
38 self.barlen = 64 - self.counters_width
39
40 def update_granularity(self):
41 return timedelta(seconds=0.1)
42
43 def update(self, current, data):
44 # Record prior for poke.
45 self.prior = (current, data)
46 self.atLineStart = False
47
48 # Build counters string.
49 sys.stdout.write('\r[')
50 for layout in self.counters_fmt:
51 Terminal.set_color(layout['color'])
52 sys.stdout.write(('%' + str(self.limit_digits) + 'd') % data[layout['value']])
53 Terminal.reset_color()
54 if layout != self.counters_fmt[-1]:
55 sys.stdout.write('|')
56 else:
57 sys.stdout.write('] ')
58
59 # Build the bar.
60 pct = int(100.0 * current / self.limit)
61 sys.stdout.write('%3d%% ' % pct)
62
63 barlen = int(1.0 * self.barlen * current / self.limit) - 1
64 bar = '=' * barlen + '>' + ' ' * (self.barlen - barlen - 1)
65 sys.stdout.write(bar + '|')
66
67 # Update the bar.
68 dt = datetime.now() - self.t0
69 dt = dt.seconds + dt.microseconds * 1e-6
70 sys.stdout.write('%6.1fs' % dt)
71 Terminal.clear_right()
72
73 # Force redisplay, since we didn't write a \n.
74 sys.stdout.flush()
75
76 def poke(self):
77 if not self.prior:
78 return
79 self.update(*self.prior)
80
81 def finish(self, complete=True):
82 final_count = self.limit if complete else self.prior[0]
83 self.update(final_count, self.prior[1])
84 sys.stdout.write('\n')
85
86 def beginline(self):
87 if not self.atLineStart:
88 sys.stdout.write('\n')
89 self.atLineStart = True
90
91 def message(self, msg):
92 self.beginline()
93 sys.stdout.write(msg)
94 sys.stdout.write('\n')
95
96 @staticmethod
97 def conservative_isatty():
98 """
99 Prefer erring on the side of caution and not using terminal commands if
100 the current output stream may be a file. We explicitly check for the
101 Android platform because terminal commands work poorly over ADB's
102 redirection.
103 """
104 try:
105 import android
106 return False
107 except ImportError:
108 return sys.stdout.isatty()
109 return False

mercurial