##// END OF EJS Templates
configitems: register the 'progress.estimate' config
marmoute -
r33247:bbc57a7e default
parent child Browse files
Show More
@@ -1,207 +1,210 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 10 import functools
11 11
12 12 from . import (
13 13 error,
14 14 )
15 15
16 16 def loadconfigtable(ui, extname, configtable):
17 17 """update config item known to the ui with the extension ones"""
18 18 for section, items in configtable.items():
19 19 knownitems = ui._knownconfig.setdefault(section, {})
20 20 knownkeys = set(knownitems)
21 21 newkeys = set(items)
22 22 for key in sorted(knownkeys & newkeys):
23 23 msg = "extension '%s' overwrite config item '%s.%s'"
24 24 msg %= (extname, section, key)
25 25 ui.develwarn(msg, config='warn-config')
26 26
27 27 knownitems.update(items)
28 28
29 29 class configitem(object):
30 30 """represent a known config item
31 31
32 32 :section: the official config section where to find this item,
33 33 :name: the official name within the section,
34 34 :default: default value for this item,
35 35 """
36 36
37 37 def __init__(self, section, name, default=None):
38 38 self.section = section
39 39 self.name = name
40 40 self.default = default
41 41
42 42 coreitems = {}
43 43
44 44 def _register(configtable, *args, **kwargs):
45 45 item = configitem(*args, **kwargs)
46 46 section = configtable.setdefault(item.section, {})
47 47 if item.name in section:
48 48 msg = "duplicated config item registration for '%s.%s'"
49 49 raise error.ProgrammingError(msg % (item.section, item.name))
50 50 section[item.name] = item
51 51
52 52 # Registering actual config items
53 53
54 54 def getitemregister(configtable):
55 55 return functools.partial(_register, configtable)
56 56
57 57 coreconfigitem = getitemregister(coreitems)
58 58
59 59 coreconfigitem('auth', 'cookiefile',
60 60 default=None,
61 61 )
62 62 # bookmarks.pushing: internal hack for discovery
63 63 coreconfigitem('bookmarks', 'pushing',
64 64 default=list,
65 65 )
66 66 # bundle.mainreporoot: internal hack for bundlerepo
67 67 coreconfigitem('bundle', 'mainreporoot',
68 68 default='',
69 69 )
70 70 # bundle.reorder: experimental config
71 71 coreconfigitem('bundle', 'reorder',
72 72 default='auto',
73 73 )
74 74 coreconfigitem('color', 'mode',
75 75 default='auto',
76 76 )
77 77 coreconfigitem('devel', 'all-warnings',
78 78 default=False,
79 79 )
80 80 coreconfigitem('devel', 'bundle2.debug',
81 81 default=False,
82 82 )
83 83 coreconfigitem('devel', 'check-locks',
84 84 default=False,
85 85 )
86 86 coreconfigitem('devel', 'check-relroot',
87 87 default=False,
88 88 )
89 89 coreconfigitem('devel', 'disableloaddefaultcerts',
90 90 default=False,
91 91 )
92 92 coreconfigitem('devel', 'legacy.exchange',
93 93 default=list,
94 94 )
95 95 coreconfigitem('devel', 'servercafile',
96 96 default='',
97 97 )
98 98 coreconfigitem('devel', 'serverexactprotocol',
99 99 default='',
100 100 )
101 101 coreconfigitem('devel', 'serverrequirecert',
102 102 default=False,
103 103 )
104 104 coreconfigitem('devel', 'strip-obsmarkers',
105 105 default=True,
106 106 )
107 107 coreconfigitem('format', 'aggressivemergedeltas',
108 108 default=False,
109 109 )
110 110 coreconfigitem('format', 'chunkcachesize',
111 111 default=None,
112 112 )
113 113 coreconfigitem('format', 'dotencode',
114 114 default=True,
115 115 )
116 116 coreconfigitem('format', 'generaldelta',
117 117 default=False,
118 118 )
119 119 coreconfigitem('format', 'manifestcachesize',
120 120 default=None,
121 121 )
122 122 coreconfigitem('format', 'maxchainlen',
123 123 default=None,
124 124 )
125 125 coreconfigitem('format', 'obsstore-version',
126 126 default=None,
127 127 )
128 128 coreconfigitem('format', 'usefncache',
129 129 default=True,
130 130 )
131 131 coreconfigitem('format', 'usegeneraldelta',
132 132 default=True,
133 133 )
134 134 coreconfigitem('format', 'usestore',
135 135 default=True,
136 136 )
137 137 coreconfigitem('hostsecurity', 'ciphers',
138 138 default=None,
139 139 )
140 140 coreconfigitem('hostsecurity', 'disabletls10warning',
141 141 default=False,
142 142 )
143 143 coreconfigitem('patch', 'eol',
144 144 default='strict',
145 145 )
146 146 coreconfigitem('patch', 'fuzz',
147 147 default=2,
148 148 )
149 149 coreconfigitem('progress', 'assume-tty',
150 150 default=False,
151 151 )
152 152 coreconfigitem('progress', 'clear-complete',
153 153 default=True,
154 154 )
155 coreconfigitem('progress', 'estimate',
156 default=2,
157 )
155 158 coreconfigitem('server', 'bundle1',
156 159 default=True,
157 160 )
158 161 coreconfigitem('server', 'bundle1gd',
159 162 default=None,
160 163 )
161 164 coreconfigitem('server', 'compressionengines',
162 165 default=list,
163 166 )
164 167 coreconfigitem('server', 'concurrent-push-mode',
165 168 default='strict',
166 169 )
167 170 coreconfigitem('server', 'disablefullbundle',
168 171 default=False,
169 172 )
170 173 coreconfigitem('server', 'maxhttpheaderlen',
171 174 default=1024,
172 175 )
173 176 coreconfigitem('server', 'preferuncompressed',
174 177 default=False,
175 178 )
176 179 coreconfigitem('server', 'uncompressedallowsecret',
177 180 default=False,
178 181 )
179 182 coreconfigitem('server', 'validate',
180 183 default=False,
181 184 )
182 185 coreconfigitem('server', 'zliblevel',
183 186 default=-1,
184 187 )
185 188 coreconfigitem('ui', 'clonebundleprefers',
186 189 default=list,
187 190 )
188 191 coreconfigitem('ui', 'interactive',
189 192 default=None,
190 193 )
191 194 coreconfigitem('ui', 'quiet',
192 195 default=False,
193 196 )
194 197 # Windows defaults to a limit of 512 open files. A buffer of 128
195 198 # should give us enough headway.
196 199 coreconfigitem('worker', 'backgroundclosemaxqueue',
197 200 default=384,
198 201 )
199 202 coreconfigitem('worker', 'backgroundcloseminfilecount',
200 203 default=2048,
201 204 )
202 205 coreconfigitem('worker', 'backgroundclosethreadcount',
203 206 default=4,
204 207 )
205 208 coreconfigitem('worker', 'numcpus',
206 209 default=None,
207 210 )
@@ -1,280 +1,280 b''
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 10 import errno
11 11 import threading
12 12 import time
13 13
14 14 from .i18n import _
15 15 from . import encoding
16 16
17 17 def spacejoin(*args):
18 18 return ' '.join(s for s in args if s)
19 19
20 20 def shouldprint(ui):
21 21 return not (ui.quiet or ui.plain('progress')) and (
22 22 ui._isatty(ui.ferr) or ui.configbool('progress', 'assume-tty'))
23 23
24 24 def fmtremaining(seconds):
25 25 """format a number of remaining seconds in human readable way
26 26
27 27 This will properly display seconds, minutes, hours, days if needed"""
28 28 if seconds < 60:
29 29 # i18n: format XX seconds as "XXs"
30 30 return _("%02ds") % (seconds)
31 31 minutes = seconds // 60
32 32 if minutes < 60:
33 33 seconds -= minutes * 60
34 34 # i18n: format X minutes and YY seconds as "XmYYs"
35 35 return _("%dm%02ds") % (minutes, seconds)
36 36 # we're going to ignore seconds in this case
37 37 minutes += 1
38 38 hours = minutes // 60
39 39 minutes -= hours * 60
40 40 if hours < 30:
41 41 # i18n: format X hours and YY minutes as "XhYYm"
42 42 return _("%dh%02dm") % (hours, minutes)
43 43 # we're going to ignore minutes in this case
44 44 hours += 1
45 45 days = hours // 24
46 46 hours -= days * 24
47 47 if days < 15:
48 48 # i18n: format X days and YY hours as "XdYYh"
49 49 return _("%dd%02dh") % (days, hours)
50 50 # we're going to ignore hours in this case
51 51 days += 1
52 52 weeks = days // 7
53 53 days -= weeks * 7
54 54 if weeks < 55:
55 55 # i18n: format X weeks and YY days as "XwYYd"
56 56 return _("%dw%02dd") % (weeks, days)
57 57 # we're going to ignore days and treat a year as 52 weeks
58 58 weeks += 1
59 59 years = weeks // 52
60 60 weeks -= years * 52
61 61 # i18n: format X years and YY weeks as "XyYYw"
62 62 return _("%dy%02dw") % (years, weeks)
63 63
64 64 # file_write() and file_flush() of Python 2 do not restart on EINTR if
65 65 # the file is attached to a "slow" device (e.g. a terminal) and raise
66 66 # IOError. We cannot know how many bytes would be written by file_write(),
67 67 # but a progress text is known to be short enough to be written by a
68 68 # single write() syscall, so we can just retry file_write() with the whole
69 69 # text. (issue5532)
70 70 #
71 71 # This should be a short-term workaround. We'll need to fix every occurrence
72 72 # of write() to a terminal or pipe.
73 73 def _eintrretry(func, *args):
74 74 while True:
75 75 try:
76 76 return func(*args)
77 77 except IOError as err:
78 78 if err.errno == errno.EINTR:
79 79 continue
80 80 raise
81 81
82 82 class progbar(object):
83 83 def __init__(self, ui):
84 84 self.ui = ui
85 85 self._refreshlock = threading.Lock()
86 86 self.resetstate()
87 87
88 88 def resetstate(self):
89 89 self.topics = []
90 90 self.topicstates = {}
91 91 self.starttimes = {}
92 92 self.startvals = {}
93 93 self.printed = False
94 94 self.lastprint = time.time() + float(self.ui.config(
95 95 'progress', 'delay', default=3))
96 96 self.curtopic = None
97 97 self.lasttopic = None
98 98 self.indetcount = 0
99 99 self.refresh = float(self.ui.config(
100 100 'progress', 'refresh', default=0.1))
101 101 self.changedelay = max(3 * self.refresh,
102 102 float(self.ui.config(
103 103 'progress', 'changedelay', default=1)))
104 104 self.order = self.ui.configlist(
105 105 'progress', 'format',
106 106 default=['topic', 'bar', 'number', 'estimate'])
107 107
108 108 def show(self, now, topic, pos, item, unit, total):
109 109 if not shouldprint(self.ui):
110 110 return
111 111 termwidth = self.width()
112 112 self.printed = True
113 113 head = ''
114 114 needprogress = False
115 115 tail = ''
116 116 for indicator in self.order:
117 117 add = ''
118 118 if indicator == 'topic':
119 119 add = topic
120 120 elif indicator == 'number':
121 121 if total:
122 122 add = ('% ' + str(len(str(total))) +
123 123 's/%s') % (pos, total)
124 124 else:
125 125 add = str(pos)
126 126 elif indicator.startswith('item') and item:
127 127 slice = 'end'
128 128 if '-' in indicator:
129 129 wid = int(indicator.split('-')[1])
130 130 elif '+' in indicator:
131 131 slice = 'beginning'
132 132 wid = int(indicator.split('+')[1])
133 133 else:
134 134 wid = 20
135 135 if slice == 'end':
136 136 add = encoding.trim(item, wid, leftside=True)
137 137 else:
138 138 add = encoding.trim(item, wid)
139 139 add += (wid - encoding.colwidth(add)) * ' '
140 140 elif indicator == 'bar':
141 141 add = ''
142 142 needprogress = True
143 143 elif indicator == 'unit' and unit:
144 144 add = unit
145 145 elif indicator == 'estimate':
146 146 add = self.estimate(topic, pos, total, now)
147 147 elif indicator == 'speed':
148 148 add = self.speed(topic, pos, unit, now)
149 149 if not needprogress:
150 150 head = spacejoin(head, add)
151 151 else:
152 152 tail = spacejoin(tail, add)
153 153 if needprogress:
154 154 used = 0
155 155 if head:
156 156 used += encoding.colwidth(head) + 1
157 157 if tail:
158 158 used += encoding.colwidth(tail) + 1
159 159 progwidth = termwidth - used - 3
160 160 if total and pos <= total:
161 161 amt = pos * progwidth // total
162 162 bar = '=' * (amt - 1)
163 163 if amt > 0:
164 164 bar += '>'
165 165 bar += ' ' * (progwidth - amt)
166 166 else:
167 167 progwidth -= 3
168 168 self.indetcount += 1
169 169 # mod the count by twice the width so we can make the
170 170 # cursor bounce between the right and left sides
171 171 amt = self.indetcount % (2 * progwidth)
172 172 amt -= progwidth
173 173 bar = (' ' * int(progwidth - abs(amt)) + '<=>' +
174 174 ' ' * int(abs(amt)))
175 175 prog = ''.join(('[', bar , ']'))
176 176 out = spacejoin(head, prog, tail)
177 177 else:
178 178 out = spacejoin(head, tail)
179 179 self._writeerr('\r' + encoding.trim(out, termwidth))
180 180 self.lasttopic = topic
181 181 self._flusherr()
182 182
183 183 def clear(self):
184 184 if not self.printed or not self.lastprint or not shouldprint(self.ui):
185 185 return
186 186 self._writeerr('\r%s\r' % (' ' * self.width()))
187 187 if self.printed:
188 188 # force immediate re-paint of progress bar
189 189 self.lastprint = 0
190 190
191 191 def complete(self):
192 192 if not shouldprint(self.ui):
193 193 return
194 194 if self.ui.configbool('progress', 'clear-complete'):
195 195 self.clear()
196 196 else:
197 197 self._writeerr('\n')
198 198 self._flusherr()
199 199
200 200 def _flusherr(self):
201 201 _eintrretry(self.ui.ferr.flush)
202 202
203 203 def _writeerr(self, msg):
204 204 _eintrretry(self.ui.ferr.write, msg)
205 205
206 206 def width(self):
207 207 tw = self.ui.termwidth()
208 208 return min(int(self.ui.config('progress', 'width', default=tw)), tw)
209 209
210 210 def estimate(self, topic, pos, total, now):
211 211 if total is None:
212 212 return ''
213 213 initialpos = self.startvals[topic]
214 214 target = total - initialpos
215 215 delta = pos - initialpos
216 216 if delta > 0:
217 217 elapsed = now - self.starttimes[topic]
218 218 # experimental config: progress.estimate
219 219 if elapsed > float(
220 self.ui.config('progress', 'estimate', default=2)):
220 self.ui.config('progress', 'estimate')):
221 221 seconds = (elapsed * (target - delta)) // delta + 1
222 222 return fmtremaining(seconds)
223 223 return ''
224 224
225 225 def speed(self, topic, pos, unit, now):
226 226 initialpos = self.startvals[topic]
227 227 delta = pos - initialpos
228 228 elapsed = now - self.starttimes[topic]
229 229 if elapsed > float(
230 self.ui.config('progress', 'estimate', default=2)):
230 self.ui.config('progress', 'estimate')):
231 231 return _('%d %s/sec') % (delta / elapsed, unit)
232 232 return ''
233 233
234 234 def _oktoprint(self, now):
235 235 '''Check if conditions are met to print - e.g. changedelay elapsed'''
236 236 if (self.lasttopic is None # first time we printed
237 237 # not a topic change
238 238 or self.curtopic == self.lasttopic
239 239 # it's been long enough we should print anyway
240 240 or now - self.lastprint >= self.changedelay):
241 241 return True
242 242 else:
243 243 return False
244 244
245 245 def progress(self, topic, pos, item='', unit='', total=None):
246 246 now = time.time()
247 247 self._refreshlock.acquire()
248 248 try:
249 249 if pos is None:
250 250 self.starttimes.pop(topic, None)
251 251 self.startvals.pop(topic, None)
252 252 self.topicstates.pop(topic, None)
253 253 # reset the progress bar if this is the outermost topic
254 254 if self.topics and self.topics[0] == topic and self.printed:
255 255 self.complete()
256 256 self.resetstate()
257 257 # truncate the list of topics assuming all topics within
258 258 # this one are also closed
259 259 if topic in self.topics:
260 260 self.topics = self.topics[:self.topics.index(topic)]
261 261 # reset the last topic to the one we just unwound to,
262 262 # so that higher-level topics will be stickier than
263 263 # lower-level topics
264 264 if self.topics:
265 265 self.lasttopic = self.topics[-1]
266 266 else:
267 267 self.lasttopic = None
268 268 else:
269 269 if topic not in self.topics:
270 270 self.starttimes[topic] = now
271 271 self.startvals[topic] = pos
272 272 self.topics.append(topic)
273 273 self.topicstates[topic] = pos, item, unit, total
274 274 self.curtopic = topic
275 275 if now - self.lastprint >= self.refresh and self.topics:
276 276 if self._oktoprint(now):
277 277 self.lastprint = now
278 278 self.show(now, topic, *self.topicstates[topic])
279 279 finally:
280 280 self._refreshlock.release()
General Comments 0
You need to be logged in to leave comments. Login now