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