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