##// END OF EJS Templates
progress: move current topic to member variable
Solomon Matthews -
r23906:e0ae0a4e default
parent child Browse files
Show More
@@ -1,308 +1,310 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 self.curtopic = None
103 104 self.lasttopic = None
104 105 self.indetcount = 0
105 106 self.refresh = float(self.ui.config(
106 107 'progress', 'refresh', default=0.1))
107 108 self.changedelay = max(3 * self.refresh,
108 109 float(self.ui.config(
109 110 'progress', 'changedelay', default=1)))
110 111 self.order = self.ui.configlist(
111 112 'progress', 'format',
112 113 default=['topic', 'bar', 'number', 'estimate'])
113 114
114 115 def show(self, now, topic, pos, item, unit, total):
115 116 if not shouldprint(self.ui):
116 117 return
117 118 termwidth = self.width()
118 119 self.printed = True
119 120 head = ''
120 121 needprogress = False
121 122 tail = ''
122 123 for indicator in self.order:
123 124 add = ''
124 125 if indicator == 'topic':
125 126 add = topic
126 127 elif indicator == 'number':
127 128 if total:
128 129 add = ('% ' + str(len(str(total))) +
129 130 's/%s') % (pos, total)
130 131 else:
131 132 add = str(pos)
132 133 elif indicator.startswith('item') and item:
133 134 slice = 'end'
134 135 if '-' in indicator:
135 136 wid = int(indicator.split('-')[1])
136 137 elif '+' in indicator:
137 138 slice = 'beginning'
138 139 wid = int(indicator.split('+')[1])
139 140 else:
140 141 wid = 20
141 142 if slice == 'end':
142 143 add = encoding.trim(item, wid, leftside=True)
143 144 else:
144 145 add = encoding.trim(item, wid)
145 146 add += (wid - encoding.colwidth(add)) * ' '
146 147 elif indicator == 'bar':
147 148 add = ''
148 149 needprogress = True
149 150 elif indicator == 'unit' and unit:
150 151 add = unit
151 152 elif indicator == 'estimate':
152 153 add = self.estimate(topic, pos, total, now)
153 154 elif indicator == 'speed':
154 155 add = self.speed(topic, pos, unit, now)
155 156 if not needprogress:
156 157 head = spacejoin(head, add)
157 158 else:
158 159 tail = spacejoin(tail, add)
159 160 if needprogress:
160 161 used = 0
161 162 if head:
162 163 used += encoding.colwidth(head) + 1
163 164 if tail:
164 165 used += encoding.colwidth(tail) + 1
165 166 progwidth = termwidth - used - 3
166 167 if total and pos <= total:
167 168 amt = pos * progwidth // total
168 169 bar = '=' * (amt - 1)
169 170 if amt > 0:
170 171 bar += '>'
171 172 bar += ' ' * (progwidth - amt)
172 173 else:
173 174 progwidth -= 3
174 175 self.indetcount += 1
175 176 # mod the count by twice the width so we can make the
176 177 # cursor bounce between the right and left sides
177 178 amt = self.indetcount % (2 * progwidth)
178 179 amt -= progwidth
179 180 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
180 181 ' ' * int(abs(amt)))
181 182 prog = ''.join(('[', bar , ']'))
182 183 out = spacejoin(head, prog, tail)
183 184 else:
184 185 out = spacejoin(head, tail)
185 186 sys.stderr.write('\r' + encoding.trim(out, termwidth))
186 187 self.lasttopic = topic
187 188 sys.stderr.flush()
188 189
189 190 def clear(self):
190 191 if not shouldprint(self.ui):
191 192 return
192 193 sys.stderr.write('\r%s\r' % (' ' * self.width()))
193 194
194 195 def complete(self):
195 196 if not shouldprint(self.ui):
196 197 return
197 198 if self.ui.configbool('progress', 'clear-complete', default=True):
198 199 self.clear()
199 200 else:
200 201 sys.stderr.write('\n')
201 202 sys.stderr.flush()
202 203
203 204 def width(self):
204 205 tw = self.ui.termwidth()
205 206 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
206 207
207 208 def estimate(self, topic, pos, total, now):
208 209 if total is None:
209 210 return ''
210 211 initialpos = self.startvals[topic]
211 212 target = total - initialpos
212 213 delta = pos - initialpos
213 214 if delta > 0:
214 215 elapsed = now - self.starttimes[topic]
215 216 if elapsed > float(
216 217 self.ui.config('progress', 'estimate', default=2)):
217 218 seconds = (elapsed * (target - delta)) // delta + 1
218 219 return fmtremaining(seconds)
219 220 return ''
220 221
221 222 def speed(self, topic, pos, unit, now):
222 223 initialpos = self.startvals[topic]
223 224 delta = pos - initialpos
224 225 elapsed = now - self.starttimes[topic]
225 226 if elapsed > float(
226 227 self.ui.config('progress', 'estimate', default=2)):
227 228 return _('%d %s/sec') % (delta / elapsed, unit)
228 229 return ''
229 230
230 231 def progress(self, topic, pos, item='', unit='', total=None):
231 232 now = time.time()
232 233 try:
233 234 if pos is None:
234 235 self.starttimes.pop(topic, None)
235 236 self.startvals.pop(topic, None)
236 237 self.topicstates.pop(topic, None)
237 238 # reset the progress bar if this is the outermost topic
238 239 if self.topics and self.topics[0] == topic and self.printed:
239 240 self.complete()
240 241 self.resetstate()
241 242 # truncate the list of topics assuming all topics within
242 243 # this one are also closed
243 244 if topic in self.topics:
244 245 self.topics = self.topics[:self.topics.index(topic)]
245 246 # reset the last topic to the one we just unwound to,
246 247 # so that higher-level topics will be stickier than
247 248 # lower-level topics
248 249 if self.topics:
249 250 self.lasttopic = self.topics[-1]
250 251 else:
251 252 self.lasttopic = None
252 253 else:
253 254 if topic not in self.topics:
254 255 self.starttimes[topic] = now
255 256 self.startvals[topic] = pos
256 257 self.topics.append(topic)
257 258 self.topicstates[topic] = pos, item, unit, total
259 self.curtopic = topic
258 260 if now - self.lastprint >= self.refresh and self.topics:
259 261 if (self.lasttopic is None # first time we printed
260 262 # not a topic change
261 263 or topic == self.lasttopic
262 264 # it's been long enough we should print anyway
263 265 or now - self.lastprint >= self.changedelay):
264 266 self.lastprint = now
265 267 self.show(now, topic, *self.topicstates[topic])
266 268 finally:
267 269 pass
268 270
269 271 _singleton = None
270 272
271 273 def uisetup(ui):
272 274 global _singleton
273 275 class progressui(ui.__class__):
274 276 _progbar = None
275 277
276 278 def _quiet(self):
277 279 return self.debugflag or self.quiet
278 280
279 281 def progress(self, *args, **opts):
280 282 if not self._quiet():
281 283 self._progbar.progress(*args, **opts)
282 284 return super(progressui, self).progress(*args, **opts)
283 285
284 286 def write(self, *args, **opts):
285 287 if not self._quiet() and self._progbar.printed:
286 288 self._progbar.clear()
287 289 return super(progressui, self).write(*args, **opts)
288 290
289 291 def write_err(self, *args, **opts):
290 292 if not self._quiet() and self._progbar.printed:
291 293 self._progbar.clear()
292 294 return super(progressui, self).write_err(*args, **opts)
293 295
294 296 # Apps that derive a class from ui.ui() can use
295 297 # setconfig('progress', 'disable', 'True') to disable this extension
296 298 if ui.configbool('progress', 'disable'):
297 299 return
298 300 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
299 301 ui.__class__ = progressui
300 302 # we instantiate one globally shared progress bar to avoid
301 303 # competing progress bars when multiple UI objects get created
302 304 if not progressui._progbar:
303 305 if _singleton is None:
304 306 _singleton = progbar(ui)
305 307 progressui._progbar = _singleton
306 308
307 309 def reposetup(ui, repo):
308 310 uisetup(repo.ui)
General Comments 0
You need to be logged in to leave comments. Login now