##// END OF EJS Templates
progress: obtain stderr from ui...
Yuya Nishihara -
r30324:cb1ea3cc default
parent child Browse files
Show More
@@ -1,256 +1,255
1 1 # progress.py progress bars related code
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 from __future__ import absolute_import
9 9
10 import sys
11 10 import threading
12 11 import time
13 12
14 13 from .i18n import _
15 14 from . import encoding
16 15
17 16 def spacejoin(*args):
18 17 return ' '.join(s for s in args if s)
19 18
20 19 def shouldprint(ui):
21 20 return not (ui.quiet or ui.plain('progress')) and (
22 ui._isatty(sys.stderr) or ui.configbool('progress', 'assume-tty'))
21 ui._isatty(ui.ferr) or ui.configbool('progress', 'assume-tty'))
23 22
24 23 def fmtremaining(seconds):
25 24 """format a number of remaining seconds in human readable way
26 25
27 26 This will properly display seconds, minutes, hours, days if needed"""
28 27 if seconds < 60:
29 28 # i18n: format XX seconds as "XXs"
30 29 return _("%02ds") % (seconds)
31 30 minutes = seconds // 60
32 31 if minutes < 60:
33 32 seconds -= minutes * 60
34 33 # i18n: format X minutes and YY seconds as "XmYYs"
35 34 return _("%dm%02ds") % (minutes, seconds)
36 35 # we're going to ignore seconds in this case
37 36 minutes += 1
38 37 hours = minutes // 60
39 38 minutes -= hours * 60
40 39 if hours < 30:
41 40 # i18n: format X hours and YY minutes as "XhYYm"
42 41 return _("%dh%02dm") % (hours, minutes)
43 42 # we're going to ignore minutes in this case
44 43 hours += 1
45 44 days = hours // 24
46 45 hours -= days * 24
47 46 if days < 15:
48 47 # i18n: format X days and YY hours as "XdYYh"
49 48 return _("%dd%02dh") % (days, hours)
50 49 # we're going to ignore hours in this case
51 50 days += 1
52 51 weeks = days // 7
53 52 days -= weeks * 7
54 53 if weeks < 55:
55 54 # i18n: format X weeks and YY days as "XwYYd"
56 55 return _("%dw%02dd") % (weeks, days)
57 56 # we're going to ignore days and treat a year as 52 weeks
58 57 weeks += 1
59 58 years = weeks // 52
60 59 weeks -= years * 52
61 60 # i18n: format X years and YY weeks as "XyYYw"
62 61 return _("%dy%02dw") % (years, weeks)
63 62
64 63 class progbar(object):
65 64 def __init__(self, ui):
66 65 self.ui = ui
67 66 self._refreshlock = threading.Lock()
68 67 self.resetstate()
69 68
70 69 def resetstate(self):
71 70 self.topics = []
72 71 self.topicstates = {}
73 72 self.starttimes = {}
74 73 self.startvals = {}
75 74 self.printed = False
76 75 self.lastprint = time.time() + float(self.ui.config(
77 76 'progress', 'delay', default=3))
78 77 self.curtopic = None
79 78 self.lasttopic = None
80 79 self.indetcount = 0
81 80 self.refresh = float(self.ui.config(
82 81 'progress', 'refresh', default=0.1))
83 82 self.changedelay = max(3 * self.refresh,
84 83 float(self.ui.config(
85 84 'progress', 'changedelay', default=1)))
86 85 self.order = self.ui.configlist(
87 86 'progress', 'format',
88 87 default=['topic', 'bar', 'number', 'estimate'])
89 88
90 89 def show(self, now, topic, pos, item, unit, total):
91 90 if not shouldprint(self.ui):
92 91 return
93 92 termwidth = self.width()
94 93 self.printed = True
95 94 head = ''
96 95 needprogress = False
97 96 tail = ''
98 97 for indicator in self.order:
99 98 add = ''
100 99 if indicator == 'topic':
101 100 add = topic
102 101 elif indicator == 'number':
103 102 if total:
104 103 add = ('% ' + str(len(str(total))) +
105 104 's/%s') % (pos, total)
106 105 else:
107 106 add = str(pos)
108 107 elif indicator.startswith('item') and item:
109 108 slice = 'end'
110 109 if '-' in indicator:
111 110 wid = int(indicator.split('-')[1])
112 111 elif '+' in indicator:
113 112 slice = 'beginning'
114 113 wid = int(indicator.split('+')[1])
115 114 else:
116 115 wid = 20
117 116 if slice == 'end':
118 117 add = encoding.trim(item, wid, leftside=True)
119 118 else:
120 119 add = encoding.trim(item, wid)
121 120 add += (wid - encoding.colwidth(add)) * ' '
122 121 elif indicator == 'bar':
123 122 add = ''
124 123 needprogress = True
125 124 elif indicator == 'unit' and unit:
126 125 add = unit
127 126 elif indicator == 'estimate':
128 127 add = self.estimate(topic, pos, total, now)
129 128 elif indicator == 'speed':
130 129 add = self.speed(topic, pos, unit, now)
131 130 if not needprogress:
132 131 head = spacejoin(head, add)
133 132 else:
134 133 tail = spacejoin(tail, add)
135 134 if needprogress:
136 135 used = 0
137 136 if head:
138 137 used += encoding.colwidth(head) + 1
139 138 if tail:
140 139 used += encoding.colwidth(tail) + 1
141 140 progwidth = termwidth - used - 3
142 141 if total and pos <= total:
143 142 amt = pos * progwidth // total
144 143 bar = '=' * (amt - 1)
145 144 if amt > 0:
146 145 bar += '>'
147 146 bar += ' ' * (progwidth - amt)
148 147 else:
149 148 progwidth -= 3
150 149 self.indetcount += 1
151 150 # mod the count by twice the width so we can make the
152 151 # cursor bounce between the right and left sides
153 152 amt = self.indetcount % (2 * progwidth)
154 153 amt -= progwidth
155 154 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
156 155 ' ' * int(abs(amt)))
157 156 prog = ''.join(('[', bar , ']'))
158 157 out = spacejoin(head, prog, tail)
159 158 else:
160 159 out = spacejoin(head, tail)
161 sys.stderr.write('\r' + encoding.trim(out, termwidth))
160 self.ui.ferr.write('\r' + encoding.trim(out, termwidth))
162 161 self.lasttopic = topic
163 sys.stderr.flush()
162 self.ui.ferr.flush()
164 163
165 164 def clear(self):
166 165 if not self.printed or not self.lastprint or not shouldprint(self.ui):
167 166 return
168 sys.stderr.write('\r%s\r' % (' ' * self.width()))
167 self.ui.ferr.write('\r%s\r' % (' ' * self.width()))
169 168 if self.printed:
170 169 # force immediate re-paint of progress bar
171 170 self.lastprint = 0
172 171
173 172 def complete(self):
174 173 if not shouldprint(self.ui):
175 174 return
176 175 if self.ui.configbool('progress', 'clear-complete', default=True):
177 176 self.clear()
178 177 else:
179 sys.stderr.write('\n')
180 sys.stderr.flush()
178 self.ui.ferr.write('\n')
179 self.ui.ferr.flush()
181 180
182 181 def width(self):
183 182 tw = self.ui.termwidth()
184 183 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
185 184
186 185 def estimate(self, topic, pos, total, now):
187 186 if total is None:
188 187 return ''
189 188 initialpos = self.startvals[topic]
190 189 target = total - initialpos
191 190 delta = pos - initialpos
192 191 if delta > 0:
193 192 elapsed = now - self.starttimes[topic]
194 193 # experimental config: progress.estimate
195 194 if elapsed > float(
196 195 self.ui.config('progress', 'estimate', default=2)):
197 196 seconds = (elapsed * (target - delta)) // delta + 1
198 197 return fmtremaining(seconds)
199 198 return ''
200 199
201 200 def speed(self, topic, pos, unit, now):
202 201 initialpos = self.startvals[topic]
203 202 delta = pos - initialpos
204 203 elapsed = now - self.starttimes[topic]
205 204 if elapsed > float(
206 205 self.ui.config('progress', 'estimate', default=2)):
207 206 return _('%d %s/sec') % (delta / elapsed, unit)
208 207 return ''
209 208
210 209 def _oktoprint(self, now):
211 210 '''Check if conditions are met to print - e.g. changedelay elapsed'''
212 211 if (self.lasttopic is None # first time we printed
213 212 # not a topic change
214 213 or self.curtopic == self.lasttopic
215 214 # it's been long enough we should print anyway
216 215 or now - self.lastprint >= self.changedelay):
217 216 return True
218 217 else:
219 218 return False
220 219
221 220 def progress(self, topic, pos, item='', unit='', total=None):
222 221 now = time.time()
223 222 self._refreshlock.acquire()
224 223 try:
225 224 if pos is None:
226 225 self.starttimes.pop(topic, None)
227 226 self.startvals.pop(topic, None)
228 227 self.topicstates.pop(topic, None)
229 228 # reset the progress bar if this is the outermost topic
230 229 if self.topics and self.topics[0] == topic and self.printed:
231 230 self.complete()
232 231 self.resetstate()
233 232 # truncate the list of topics assuming all topics within
234 233 # this one are also closed
235 234 if topic in self.topics:
236 235 self.topics = self.topics[:self.topics.index(topic)]
237 236 # reset the last topic to the one we just unwound to,
238 237 # so that higher-level topics will be stickier than
239 238 # lower-level topics
240 239 if self.topics:
241 240 self.lasttopic = self.topics[-1]
242 241 else:
243 242 self.lasttopic = None
244 243 else:
245 244 if topic not in self.topics:
246 245 self.starttimes[topic] = now
247 246 self.startvals[topic] = pos
248 247 self.topics.append(topic)
249 248 self.topicstates[topic] = pos, item, unit, total
250 249 self.curtopic = topic
251 250 if now - self.lastprint >= self.refresh and self.topics:
252 251 if self._oktoprint(now):
253 252 self.lastprint = now
254 253 self.show(now, topic, *self.topicstates[topic])
255 254 finally:
256 255 self._refreshlock.release()
General Comments 0
You need to be logged in to leave comments. Login now