##// END OF EJS Templates
progress: mark experimental option
Matt Mackall -
r25847:56674fd6 default
parent child Browse files
Show More
@@ -1,251 +1,252 b''
1 1 # progress.py progress bars related code
2 2 #
3 3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import sys
9 9 import time
10 10 import threading
11 11 from mercurial import encoding
12 12
13 13 from mercurial.i18n import _
14 14
15 15
16 16 def spacejoin(*args):
17 17 return ' '.join(s for s in args if s)
18 18
19 19 def shouldprint(ui):
20 20 return not (ui.quiet or ui.plain()) and (
21 21 ui._isatty(sys.stderr) or ui.configbool('progress', 'assume-tty'))
22 22
23 23 def fmtremaining(seconds):
24 24 """format a number of remaining seconds in humain readable way
25 25
26 26 This will properly display seconds, minutes, hours, days if needed"""
27 27 if seconds < 60:
28 28 # i18n: format XX seconds as "XXs"
29 29 return _("%02ds") % (seconds)
30 30 minutes = seconds // 60
31 31 if minutes < 60:
32 32 seconds -= minutes * 60
33 33 # i18n: format X minutes and YY seconds as "XmYYs"
34 34 return _("%dm%02ds") % (minutes, seconds)
35 35 # we're going to ignore seconds in this case
36 36 minutes += 1
37 37 hours = minutes // 60
38 38 minutes -= hours * 60
39 39 if hours < 30:
40 40 # i18n: format X hours and YY minutes as "XhYYm"
41 41 return _("%dh%02dm") % (hours, minutes)
42 42 # we're going to ignore minutes in this case
43 43 hours += 1
44 44 days = hours // 24
45 45 hours -= days * 24
46 46 if days < 15:
47 47 # i18n: format X days and YY hours as "XdYYh"
48 48 return _("%dd%02dh") % (days, hours)
49 49 # we're going to ignore hours in this case
50 50 days += 1
51 51 weeks = days // 7
52 52 days -= weeks * 7
53 53 if weeks < 55:
54 54 # i18n: format X weeks and YY days as "XwYYd"
55 55 return _("%dw%02dd") % (weeks, days)
56 56 # we're going to ignore days and treat a year as 52 weeks
57 57 weeks += 1
58 58 years = weeks // 52
59 59 weeks -= years * 52
60 60 # i18n: format X years and YY weeks as "XyYYw"
61 61 return _("%dy%02dw") % (years, weeks)
62 62
63 63 class progbar(object):
64 64 def __init__(self, ui):
65 65 self.ui = ui
66 66 self._refreshlock = threading.Lock()
67 67 self.resetstate()
68 68
69 69 def resetstate(self):
70 70 self.topics = []
71 71 self.topicstates = {}
72 72 self.starttimes = {}
73 73 self.startvals = {}
74 74 self.printed = False
75 75 self.lastprint = time.time() + float(self.ui.config(
76 76 'progress', 'delay', default=3))
77 77 self.curtopic = None
78 78 self.lasttopic = None
79 79 self.indetcount = 0
80 80 self.refresh = float(self.ui.config(
81 81 'progress', 'refresh', default=0.1))
82 82 self.changedelay = max(3 * self.refresh,
83 83 float(self.ui.config(
84 84 'progress', 'changedelay', default=1)))
85 85 self.order = self.ui.configlist(
86 86 'progress', 'format',
87 87 default=['topic', 'bar', 'number', 'estimate'])
88 88
89 89 def show(self, now, topic, pos, item, unit, total):
90 90 if not shouldprint(self.ui):
91 91 return
92 92 termwidth = self.width()
93 93 self.printed = True
94 94 head = ''
95 95 needprogress = False
96 96 tail = ''
97 97 for indicator in self.order:
98 98 add = ''
99 99 if indicator == 'topic':
100 100 add = topic
101 101 elif indicator == 'number':
102 102 if total:
103 103 add = ('% ' + str(len(str(total))) +
104 104 's/%s') % (pos, total)
105 105 else:
106 106 add = str(pos)
107 107 elif indicator.startswith('item') and item:
108 108 slice = 'end'
109 109 if '-' in indicator:
110 110 wid = int(indicator.split('-')[1])
111 111 elif '+' in indicator:
112 112 slice = 'beginning'
113 113 wid = int(indicator.split('+')[1])
114 114 else:
115 115 wid = 20
116 116 if slice == 'end':
117 117 add = encoding.trim(item, wid, leftside=True)
118 118 else:
119 119 add = encoding.trim(item, wid)
120 120 add += (wid - encoding.colwidth(add)) * ' '
121 121 elif indicator == 'bar':
122 122 add = ''
123 123 needprogress = True
124 124 elif indicator == 'unit' and unit:
125 125 add = unit
126 126 elif indicator == 'estimate':
127 127 add = self.estimate(topic, pos, total, now)
128 128 elif indicator == 'speed':
129 129 add = self.speed(topic, pos, unit, now)
130 130 if not needprogress:
131 131 head = spacejoin(head, add)
132 132 else:
133 133 tail = spacejoin(tail, add)
134 134 if needprogress:
135 135 used = 0
136 136 if head:
137 137 used += encoding.colwidth(head) + 1
138 138 if tail:
139 139 used += encoding.colwidth(tail) + 1
140 140 progwidth = termwidth - used - 3
141 141 if total and pos <= total:
142 142 amt = pos * progwidth // total
143 143 bar = '=' * (amt - 1)
144 144 if amt > 0:
145 145 bar += '>'
146 146 bar += ' ' * (progwidth - amt)
147 147 else:
148 148 progwidth -= 3
149 149 self.indetcount += 1
150 150 # mod the count by twice the width so we can make the
151 151 # cursor bounce between the right and left sides
152 152 amt = self.indetcount % (2 * progwidth)
153 153 amt -= progwidth
154 154 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
155 155 ' ' * int(abs(amt)))
156 156 prog = ''.join(('[', bar , ']'))
157 157 out = spacejoin(head, prog, tail)
158 158 else:
159 159 out = spacejoin(head, tail)
160 160 sys.stderr.write('\r' + encoding.trim(out, termwidth))
161 161 self.lasttopic = topic
162 162 sys.stderr.flush()
163 163
164 164 def clear(self):
165 165 if not shouldprint(self.ui):
166 166 return
167 167 sys.stderr.write('\r%s\r' % (' ' * self.width()))
168 168
169 169 def complete(self):
170 170 if not shouldprint(self.ui):
171 171 return
172 172 if self.ui.configbool('progress', 'clear-complete', default=True):
173 173 self.clear()
174 174 else:
175 175 sys.stderr.write('\n')
176 176 sys.stderr.flush()
177 177
178 178 def width(self):
179 179 tw = self.ui.termwidth()
180 180 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
181 181
182 182 def estimate(self, topic, pos, total, now):
183 183 if total is None:
184 184 return ''
185 185 initialpos = self.startvals[topic]
186 186 target = total - initialpos
187 187 delta = pos - initialpos
188 188 if delta > 0:
189 189 elapsed = now - self.starttimes[topic]
190 # experimental config: progress.estimate
190 191 if elapsed > float(
191 192 self.ui.config('progress', 'estimate', default=2)):
192 193 seconds = (elapsed * (target - delta)) // delta + 1
193 194 return fmtremaining(seconds)
194 195 return ''
195 196
196 197 def speed(self, topic, pos, unit, now):
197 198 initialpos = self.startvals[topic]
198 199 delta = pos - initialpos
199 200 elapsed = now - self.starttimes[topic]
200 201 if elapsed > float(
201 202 self.ui.config('progress', 'estimate', default=2)):
202 203 return _('%d %s/sec') % (delta / elapsed, unit)
203 204 return ''
204 205
205 206 def _oktoprint(self, now):
206 207 '''Check if conditions are met to print - e.g. changedelay elapsed'''
207 208 if (self.lasttopic is None # first time we printed
208 209 # not a topic change
209 210 or self.curtopic == self.lasttopic
210 211 # it's been long enough we should print anyway
211 212 or now - self.lastprint >= self.changedelay):
212 213 return True
213 214 else:
214 215 return False
215 216
216 217 def progress(self, topic, pos, item='', unit='', total=None):
217 218 now = time.time()
218 219 self._refreshlock.acquire()
219 220 try:
220 221 if pos is None:
221 222 self.starttimes.pop(topic, None)
222 223 self.startvals.pop(topic, None)
223 224 self.topicstates.pop(topic, None)
224 225 # reset the progress bar if this is the outermost topic
225 226 if self.topics and self.topics[0] == topic and self.printed:
226 227 self.complete()
227 228 self.resetstate()
228 229 # truncate the list of topics assuming all topics within
229 230 # this one are also closed
230 231 if topic in self.topics:
231 232 self.topics = self.topics[:self.topics.index(topic)]
232 233 # reset the last topic to the one we just unwound to,
233 234 # so that higher-level topics will be stickier than
234 235 # lower-level topics
235 236 if self.topics:
236 237 self.lasttopic = self.topics[-1]
237 238 else:
238 239 self.lasttopic = None
239 240 else:
240 241 if topic not in self.topics:
241 242 self.starttimes[topic] = now
242 243 self.startvals[topic] = pos
243 244 self.topics.append(topic)
244 245 self.topicstates[topic] = pos, item, unit, total
245 246 self.curtopic = topic
246 247 if now - self.lastprint >= self.refresh and self.topics:
247 248 if self._oktoprint(now):
248 249 self.lastprint = now
249 250 self.show(now, topic, *self.topicstates[topic])
250 251 finally:
251 252 self._refreshlock.release()
General Comments 0
You need to be logged in to leave comments. Login now