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