##// END OF EJS Templates
progress: move update check into helper method
Solomon Matthews -
r23907:63c7783a default
parent child Browse files
Show More
@@ -1,310 +1,317 b''
1 1 # progress.py show progress bars for some actions
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 """show progress bars for some actions
9 9
10 10 This extension uses the progress information logged by hg commands
11 11 to draw progress bars that are as informative as possible. Some progress
12 12 bars only offer indeterminate information, while others have a definite
13 13 end point.
14 14
15 15 The following settings are available::
16 16
17 17 [progress]
18 18 delay = 3 # number of seconds (float) before showing the progress bar
19 19 changedelay = 1 # changedelay: minimum delay before showing a new topic.
20 20 # If set to less than 3 * refresh, that value will
21 21 # be used instead.
22 22 refresh = 0.1 # time in seconds between refreshes of the progress bar
23 23 format = topic bar number estimate # format of the progress bar
24 24 width = <none> # if set, the maximum width of the progress information
25 25 # (that is, min(width, term width) will be used)
26 26 clear-complete = True # clear the progress bar after it's done
27 27 disable = False # if true, don't show a progress bar
28 28 assume-tty = False # if true, ALWAYS show a progress bar, unless
29 29 # disable is given
30 30
31 31 Valid entries for the format field are topic, bar, number, unit,
32 32 estimate, speed, and item. item defaults to the last 20 characters of
33 33 the item, but this can be changed by adding either ``-<num>`` which
34 34 would take the last num characters, or ``+<num>`` for the first num
35 35 characters.
36 36 """
37 37
38 38 import sys
39 39 import time
40 40
41 41 from mercurial.i18n import _
42 42 testedwith = 'internal'
43 43
44 44 from mercurial import encoding
45 45
46 46 def spacejoin(*args):
47 47 return ' '.join(s for s in args if s)
48 48
49 49 def shouldprint(ui):
50 50 return not ui.plain() and (ui._isatty(sys.stderr) or
51 51 ui.configbool('progress', 'assume-tty'))
52 52
53 53 def fmtremaining(seconds):
54 54 if seconds < 60:
55 55 # i18n: format XX seconds as "XXs"
56 56 return _("%02ds") % (seconds)
57 57 minutes = seconds // 60
58 58 if minutes < 60:
59 59 seconds -= minutes * 60
60 60 # i18n: format X minutes and YY seconds as "XmYYs"
61 61 return _("%dm%02ds") % (minutes, seconds)
62 62 # we're going to ignore seconds in this case
63 63 minutes += 1
64 64 hours = minutes // 60
65 65 minutes -= hours * 60
66 66 if hours < 30:
67 67 # i18n: format X hours and YY minutes as "XhYYm"
68 68 return _("%dh%02dm") % (hours, minutes)
69 69 # we're going to ignore minutes in this case
70 70 hours += 1
71 71 days = hours // 24
72 72 hours -= days * 24
73 73 if days < 15:
74 74 # i18n: format X days and YY hours as "XdYYh"
75 75 return _("%dd%02dh") % (days, hours)
76 76 # we're going to ignore hours in this case
77 77 days += 1
78 78 weeks = days // 7
79 79 days -= weeks * 7
80 80 if weeks < 55:
81 81 # i18n: format X weeks and YY days as "XwYYd"
82 82 return _("%dw%02dd") % (weeks, days)
83 83 # we're going to ignore days and treat a year as 52 weeks
84 84 weeks += 1
85 85 years = weeks // 52
86 86 weeks -= years * 52
87 87 # i18n: format X years and YY weeks as "XyYYw"
88 88 return _("%dy%02dw") % (years, weeks)
89 89
90 90 class progbar(object):
91 91 def __init__(self, ui):
92 92 self.ui = ui
93 93 self.resetstate()
94 94
95 95 def resetstate(self):
96 96 self.topics = []
97 97 self.topicstates = {}
98 98 self.starttimes = {}
99 99 self.startvals = {}
100 100 self.printed = False
101 101 self.lastprint = time.time() + float(self.ui.config(
102 102 'progress', 'delay', default=3))
103 103 self.curtopic = None
104 104 self.lasttopic = None
105 105 self.indetcount = 0
106 106 self.refresh = float(self.ui.config(
107 107 'progress', 'refresh', default=0.1))
108 108 self.changedelay = max(3 * self.refresh,
109 109 float(self.ui.config(
110 110 'progress', 'changedelay', default=1)))
111 111 self.order = self.ui.configlist(
112 112 'progress', 'format',
113 113 default=['topic', 'bar', 'number', 'estimate'])
114 114
115 115 def show(self, now, topic, pos, item, unit, total):
116 116 if not shouldprint(self.ui):
117 117 return
118 118 termwidth = self.width()
119 119 self.printed = True
120 120 head = ''
121 121 needprogress = False
122 122 tail = ''
123 123 for indicator in self.order:
124 124 add = ''
125 125 if indicator == 'topic':
126 126 add = topic
127 127 elif indicator == 'number':
128 128 if total:
129 129 add = ('% ' + str(len(str(total))) +
130 130 's/%s') % (pos, total)
131 131 else:
132 132 add = str(pos)
133 133 elif indicator.startswith('item') and item:
134 134 slice = 'end'
135 135 if '-' in indicator:
136 136 wid = int(indicator.split('-')[1])
137 137 elif '+' in indicator:
138 138 slice = 'beginning'
139 139 wid = int(indicator.split('+')[1])
140 140 else:
141 141 wid = 20
142 142 if slice == 'end':
143 143 add = encoding.trim(item, wid, leftside=True)
144 144 else:
145 145 add = encoding.trim(item, wid)
146 146 add += (wid - encoding.colwidth(add)) * ' '
147 147 elif indicator == 'bar':
148 148 add = ''
149 149 needprogress = True
150 150 elif indicator == 'unit' and unit:
151 151 add = unit
152 152 elif indicator == 'estimate':
153 153 add = self.estimate(topic, pos, total, now)
154 154 elif indicator == 'speed':
155 155 add = self.speed(topic, pos, unit, now)
156 156 if not needprogress:
157 157 head = spacejoin(head, add)
158 158 else:
159 159 tail = spacejoin(tail, add)
160 160 if needprogress:
161 161 used = 0
162 162 if head:
163 163 used += encoding.colwidth(head) + 1
164 164 if tail:
165 165 used += encoding.colwidth(tail) + 1
166 166 progwidth = termwidth - used - 3
167 167 if total and pos <= total:
168 168 amt = pos * progwidth // total
169 169 bar = '=' * (amt - 1)
170 170 if amt > 0:
171 171 bar += '>'
172 172 bar += ' ' * (progwidth - amt)
173 173 else:
174 174 progwidth -= 3
175 175 self.indetcount += 1
176 176 # mod the count by twice the width so we can make the
177 177 # cursor bounce between the right and left sides
178 178 amt = self.indetcount % (2 * progwidth)
179 179 amt -= progwidth
180 180 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
181 181 ' ' * int(abs(amt)))
182 182 prog = ''.join(('[', bar , ']'))
183 183 out = spacejoin(head, prog, tail)
184 184 else:
185 185 out = spacejoin(head, tail)
186 186 sys.stderr.write('\r' + encoding.trim(out, termwidth))
187 187 self.lasttopic = topic
188 188 sys.stderr.flush()
189 189
190 190 def clear(self):
191 191 if not shouldprint(self.ui):
192 192 return
193 193 sys.stderr.write('\r%s\r' % (' ' * self.width()))
194 194
195 195 def complete(self):
196 196 if not shouldprint(self.ui):
197 197 return
198 198 if self.ui.configbool('progress', 'clear-complete', default=True):
199 199 self.clear()
200 200 else:
201 201 sys.stderr.write('\n')
202 202 sys.stderr.flush()
203 203
204 204 def width(self):
205 205 tw = self.ui.termwidth()
206 206 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
207 207
208 208 def estimate(self, topic, pos, total, now):
209 209 if total is None:
210 210 return ''
211 211 initialpos = self.startvals[topic]
212 212 target = total - initialpos
213 213 delta = pos - initialpos
214 214 if delta > 0:
215 215 elapsed = now - self.starttimes[topic]
216 216 if elapsed > float(
217 217 self.ui.config('progress', 'estimate', default=2)):
218 218 seconds = (elapsed * (target - delta)) // delta + 1
219 219 return fmtremaining(seconds)
220 220 return ''
221 221
222 222 def speed(self, topic, pos, unit, now):
223 223 initialpos = self.startvals[topic]
224 224 delta = pos - initialpos
225 225 elapsed = now - self.starttimes[topic]
226 226 if elapsed > float(
227 227 self.ui.config('progress', 'estimate', default=2)):
228 228 return _('%d %s/sec') % (delta / elapsed, unit)
229 229 return ''
230 230
231 def _oktoprint(self, now):
232 '''Check if conditions are met to print - e.g. changedelay elapsed'''
233 if (self.lasttopic is None # first time we printed
234 # not a topic change
235 or self.curtopic == self.lasttopic
236 # it's been long enough we should print anyway
237 or now - self.lastprint >= self.changedelay):
238 return True
239 else:
240 return False
241
231 242 def progress(self, topic, pos, item='', unit='', total=None):
232 243 now = time.time()
233 244 try:
234 245 if pos is None:
235 246 self.starttimes.pop(topic, None)
236 247 self.startvals.pop(topic, None)
237 248 self.topicstates.pop(topic, None)
238 249 # reset the progress bar if this is the outermost topic
239 250 if self.topics and self.topics[0] == topic and self.printed:
240 251 self.complete()
241 252 self.resetstate()
242 253 # truncate the list of topics assuming all topics within
243 254 # this one are also closed
244 255 if topic in self.topics:
245 256 self.topics = self.topics[:self.topics.index(topic)]
246 257 # reset the last topic to the one we just unwound to,
247 258 # so that higher-level topics will be stickier than
248 259 # lower-level topics
249 260 if self.topics:
250 261 self.lasttopic = self.topics[-1]
251 262 else:
252 263 self.lasttopic = None
253 264 else:
254 265 if topic not in self.topics:
255 266 self.starttimes[topic] = now
256 267 self.startvals[topic] = pos
257 268 self.topics.append(topic)
258 269 self.topicstates[topic] = pos, item, unit, total
259 270 self.curtopic = topic
260 271 if now - self.lastprint >= self.refresh and self.topics:
261 if (self.lasttopic is None # first time we printed
262 # not a topic change
263 or topic == self.lasttopic
264 # it's been long enough we should print anyway
265 or now - self.lastprint >= self.changedelay):
272 if self._oktoprint(now):
266 273 self.lastprint = now
267 274 self.show(now, topic, *self.topicstates[topic])
268 275 finally:
269 276 pass
270 277
271 278 _singleton = None
272 279
273 280 def uisetup(ui):
274 281 global _singleton
275 282 class progressui(ui.__class__):
276 283 _progbar = None
277 284
278 285 def _quiet(self):
279 286 return self.debugflag or self.quiet
280 287
281 288 def progress(self, *args, **opts):
282 289 if not self._quiet():
283 290 self._progbar.progress(*args, **opts)
284 291 return super(progressui, self).progress(*args, **opts)
285 292
286 293 def write(self, *args, **opts):
287 294 if not self._quiet() and self._progbar.printed:
288 295 self._progbar.clear()
289 296 return super(progressui, self).write(*args, **opts)
290 297
291 298 def write_err(self, *args, **opts):
292 299 if not self._quiet() and self._progbar.printed:
293 300 self._progbar.clear()
294 301 return super(progressui, self).write_err(*args, **opts)
295 302
296 303 # Apps that derive a class from ui.ui() can use
297 304 # setconfig('progress', 'disable', 'True') to disable this extension
298 305 if ui.configbool('progress', 'disable'):
299 306 return
300 307 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
301 308 ui.__class__ = progressui
302 309 # we instantiate one globally shared progress bar to avoid
303 310 # competing progress bars when multiple UI objects get created
304 311 if not progressui._progbar:
305 312 if _singleton is None:
306 313 _singleton = progbar(ui)
307 314 progressui._progbar = _singleton
308 315
309 316 def reposetup(ui, repo):
310 317 uisetup(repo.ui)
General Comments 0
You need to be logged in to leave comments. Login now