##// END OF EJS Templates
progress: use ui._isatty
Matt Mackall -
r16753:9cca7b70 default
parent child Browse files
Show More
@@ -1,296 +1,295
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 from mercurial import util
42 41 from mercurial.i18n import _
43 42 testedwith = 'internal'
44 43
45 44 def spacejoin(*args):
46 45 return ' '.join(s for s in args if s)
47 46
48 47 def shouldprint(ui):
49 return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
48 return ui._isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
50 49
51 50 def fmtremaining(seconds):
52 51 if seconds < 60:
53 52 # i18n: format XX seconds as "XXs"
54 53 return _("%02ds") % (seconds)
55 54 minutes = seconds // 60
56 55 if minutes < 60:
57 56 seconds -= minutes * 60
58 57 # i18n: format X minutes and YY seconds as "XmYYs"
59 58 return _("%dm%02ds") % (minutes, seconds)
60 59 # we're going to ignore seconds in this case
61 60 minutes += 1
62 61 hours = minutes // 60
63 62 minutes -= hours * 60
64 63 if hours < 30:
65 64 # i18n: format X hours and YY minutes as "XhYYm"
66 65 return _("%dh%02dm") % (hours, minutes)
67 66 # we're going to ignore minutes in this case
68 67 hours += 1
69 68 days = hours // 24
70 69 hours -= days * 24
71 70 if days < 15:
72 71 # i18n: format X days and YY hours as "XdYYh"
73 72 return _("%dd%02dh") % (days, hours)
74 73 # we're going to ignore hours in this case
75 74 days += 1
76 75 weeks = days // 7
77 76 days -= weeks * 7
78 77 if weeks < 55:
79 78 # i18n: format X weeks and YY days as "XwYYd"
80 79 return _("%dw%02dd") % (weeks, days)
81 80 # we're going to ignore days and treat a year as 52 weeks
82 81 weeks += 1
83 82 years = weeks // 52
84 83 weeks -= years * 52
85 84 # i18n: format X years and YY weeks as "XyYYw"
86 85 return _("%dy%02dw") % (years, weeks)
87 86
88 87 class progbar(object):
89 88 def __init__(self, ui):
90 89 self.ui = ui
91 90 self.resetstate()
92 91
93 92 def resetstate(self):
94 93 self.topics = []
95 94 self.topicstates = {}
96 95 self.starttimes = {}
97 96 self.startvals = {}
98 97 self.printed = False
99 98 self.lastprint = time.time() + float(self.ui.config(
100 99 'progress', 'delay', default=3))
101 100 self.lasttopic = None
102 101 self.indetcount = 0
103 102 self.refresh = float(self.ui.config(
104 103 'progress', 'refresh', default=0.1))
105 104 self.changedelay = max(3 * self.refresh,
106 105 float(self.ui.config(
107 106 'progress', 'changedelay', default=1)))
108 107 self.order = self.ui.configlist(
109 108 'progress', 'format',
110 109 default=['topic', 'bar', 'number', 'estimate'])
111 110
112 111 def show(self, now, topic, pos, item, unit, total):
113 112 if not shouldprint(self.ui):
114 113 return
115 114 termwidth = self.width()
116 115 self.printed = True
117 116 head = ''
118 117 needprogress = False
119 118 tail = ''
120 119 for indicator in self.order:
121 120 add = ''
122 121 if indicator == 'topic':
123 122 add = topic
124 123 elif indicator == 'number':
125 124 if total:
126 125 add = ('% ' + str(len(str(total))) +
127 126 's/%s') % (pos, total)
128 127 else:
129 128 add = str(pos)
130 129 elif indicator.startswith('item') and item:
131 130 slice = 'end'
132 131 if '-' in indicator:
133 132 wid = int(indicator.split('-')[1])
134 133 elif '+' in indicator:
135 134 slice = 'beginning'
136 135 wid = int(indicator.split('+')[1])
137 136 else:
138 137 wid = 20
139 138 if slice == 'end':
140 139 add = item[-wid:]
141 140 else:
142 141 add = item[:wid]
143 142 add += (wid - len(add)) * ' '
144 143 elif indicator == 'bar':
145 144 add = ''
146 145 needprogress = True
147 146 elif indicator == 'unit' and unit:
148 147 add = unit
149 148 elif indicator == 'estimate':
150 149 add = self.estimate(topic, pos, total, now)
151 150 elif indicator == 'speed':
152 151 add = self.speed(topic, pos, unit, now)
153 152 if not needprogress:
154 153 head = spacejoin(head, add)
155 154 else:
156 155 tail = spacejoin(tail, add)
157 156 if needprogress:
158 157 used = 0
159 158 if head:
160 159 used += len(head) + 1
161 160 if tail:
162 161 used += len(tail) + 1
163 162 progwidth = termwidth - used - 3
164 163 if total and pos <= total:
165 164 amt = pos * progwidth // total
166 165 bar = '=' * (amt - 1)
167 166 if amt > 0:
168 167 bar += '>'
169 168 bar += ' ' * (progwidth - amt)
170 169 else:
171 170 progwidth -= 3
172 171 self.indetcount += 1
173 172 # mod the count by twice the width so we can make the
174 173 # cursor bounce between the right and left sides
175 174 amt = self.indetcount % (2 * progwidth)
176 175 amt -= progwidth
177 176 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
178 177 ' ' * int(abs(amt)))
179 178 prog = ''.join(('[', bar , ']'))
180 179 out = spacejoin(head, prog, tail)
181 180 else:
182 181 out = spacejoin(head, tail)
183 182 sys.stderr.write('\r' + out[:termwidth])
184 183 self.lasttopic = topic
185 184 sys.stderr.flush()
186 185
187 186 def clear(self):
188 187 if not shouldprint(self.ui):
189 188 return
190 189 sys.stderr.write('\r%s\r' % (' ' * self.width()))
191 190
192 191 def complete(self):
193 192 if not shouldprint(self.ui):
194 193 return
195 194 if self.ui.configbool('progress', 'clear-complete', default=True):
196 195 self.clear()
197 196 else:
198 197 sys.stderr.write('\n')
199 198 sys.stderr.flush()
200 199
201 200 def width(self):
202 201 tw = self.ui.termwidth()
203 202 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
204 203
205 204 def estimate(self, topic, pos, total, now):
206 205 if total is None:
207 206 return ''
208 207 initialpos = self.startvals[topic]
209 208 target = total - initialpos
210 209 delta = pos - initialpos
211 210 if delta > 0:
212 211 elapsed = now - self.starttimes[topic]
213 212 if elapsed > float(
214 213 self.ui.config('progress', 'estimate', default=2)):
215 214 seconds = (elapsed * (target - delta)) // delta + 1
216 215 return fmtremaining(seconds)
217 216 return ''
218 217
219 218 def speed(self, topic, pos, unit, now):
220 219 initialpos = self.startvals[topic]
221 220 delta = pos - initialpos
222 221 elapsed = now - self.starttimes[topic]
223 222 if elapsed > float(
224 223 self.ui.config('progress', 'estimate', default=2)):
225 224 return _('%d %s/sec') % (delta / elapsed, unit)
226 225 return ''
227 226
228 227 def progress(self, topic, pos, item='', unit='', total=None):
229 228 now = time.time()
230 229 if pos is None:
231 230 self.starttimes.pop(topic, None)
232 231 self.startvals.pop(topic, None)
233 232 self.topicstates.pop(topic, None)
234 233 # reset the progress bar if this is the outermost topic
235 234 if self.topics and self.topics[0] == topic and self.printed:
236 235 self.complete()
237 236 self.resetstate()
238 237 # truncate the list of topics assuming all topics within
239 238 # this one are also closed
240 239 if topic in self.topics:
241 240 self.topics = self.topics[:self.topics.index(topic)]
242 241 else:
243 242 if topic not in self.topics:
244 243 self.starttimes[topic] = now
245 244 self.startvals[topic] = pos
246 245 self.topics.append(topic)
247 246 self.topicstates[topic] = pos, item, unit, total
248 247 if now - self.lastprint >= self.refresh and self.topics:
249 248 if (self.lasttopic is None # first time we printed
250 249 # not a topic change
251 250 or topic == self.lasttopic
252 251 # it's been long enough we should print anyway
253 252 or now - self.lastprint >= self.changedelay):
254 253 self.lastprint = now
255 254 self.show(now, topic, *self.topicstates[topic])
256 255
257 256 _singleton = None
258 257
259 258 def uisetup(ui):
260 259 global _singleton
261 260 class progressui(ui.__class__):
262 261 _progbar = None
263 262
264 263 def _quiet(self):
265 264 return self.debugflag or self.quiet
266 265
267 266 def progress(self, *args, **opts):
268 267 if not self._quiet():
269 268 self._progbar.progress(*args, **opts)
270 269 return super(progressui, self).progress(*args, **opts)
271 270
272 271 def write(self, *args, **opts):
273 272 if not self._quiet() and self._progbar.printed:
274 273 self._progbar.clear()
275 274 return super(progressui, self).write(*args, **opts)
276 275
277 276 def write_err(self, *args, **opts):
278 277 if not self._quiet() and self._progbar.printed:
279 278 self._progbar.clear()
280 279 return super(progressui, self).write_err(*args, **opts)
281 280
282 281 # Apps that derive a class from ui.ui() can use
283 282 # setconfig('progress', 'disable', 'True') to disable this extension
284 283 if ui.configbool('progress', 'disable'):
285 284 return
286 285 if shouldprint(ui) and not ui.debugflag and not ui.quiet:
287 286 ui.__class__ = progressui
288 287 # we instantiate one globally shared progress bar to avoid
289 288 # competing progress bars when multiple UI objects get created
290 289 if not progressui._progbar:
291 290 if _singleton is None:
292 291 _singleton = progbar(ui)
293 292 progressui._progbar = _singleton
294 293
295 294 def reposetup(ui, repo):
296 295 uisetup(repo.ui)
General Comments 0
You need to be logged in to leave comments. Login now