##// END OF EJS Templates
ui: add a configbytes method, for space configuration...
Bryan O'Sullivan -
r19065:2c4cd1c4 default
parent child Browse files
Show More
@@ -1,773 +1,812 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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 i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, re, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22 self.callhooks = True
22 self.callhooks = True
23
23
24 if src:
24 if src:
25 self.fout = src.fout
25 self.fout = src.fout
26 self.ferr = src.ferr
26 self.ferr = src.ferr
27 self.fin = src.fin
27 self.fin = src.fin
28
28
29 self._tcfg = src._tcfg.copy()
29 self._tcfg = src._tcfg.copy()
30 self._ucfg = src._ucfg.copy()
30 self._ucfg = src._ucfg.copy()
31 self._ocfg = src._ocfg.copy()
31 self._ocfg = src._ocfg.copy()
32 self._trustusers = src._trustusers.copy()
32 self._trustusers = src._trustusers.copy()
33 self._trustgroups = src._trustgroups.copy()
33 self._trustgroups = src._trustgroups.copy()
34 self.environ = src.environ
34 self.environ = src.environ
35 self.callhooks = src.callhooks
35 self.callhooks = src.callhooks
36 self.fixconfig()
36 self.fixconfig()
37 else:
37 else:
38 self.fout = sys.stdout
38 self.fout = sys.stdout
39 self.ferr = sys.stderr
39 self.ferr = sys.stderr
40 self.fin = sys.stdin
40 self.fin = sys.stdin
41
41
42 # shared read-only environment
42 # shared read-only environment
43 self.environ = os.environ
43 self.environ = os.environ
44 # we always trust global config files
44 # we always trust global config files
45 for f in scmutil.rcpath():
45 for f in scmutil.rcpath():
46 self.readconfig(f, trust=True)
46 self.readconfig(f, trust=True)
47
47
48 def copy(self):
48 def copy(self):
49 return self.__class__(self)
49 return self.__class__(self)
50
50
51 def formatter(self, topic, opts):
51 def formatter(self, topic, opts):
52 return formatter.formatter(self, topic, opts)
52 return formatter.formatter(self, topic, opts)
53
53
54 def _trusted(self, fp, f):
54 def _trusted(self, fp, f):
55 st = util.fstat(fp)
55 st = util.fstat(fp)
56 if util.isowner(st):
56 if util.isowner(st):
57 return True
57 return True
58
58
59 tusers, tgroups = self._trustusers, self._trustgroups
59 tusers, tgroups = self._trustusers, self._trustgroups
60 if '*' in tusers or '*' in tgroups:
60 if '*' in tusers or '*' in tgroups:
61 return True
61 return True
62
62
63 user = util.username(st.st_uid)
63 user = util.username(st.st_uid)
64 group = util.groupname(st.st_gid)
64 group = util.groupname(st.st_gid)
65 if user in tusers or group in tgroups or user == util.username():
65 if user in tusers or group in tgroups or user == util.username():
66 return True
66 return True
67
67
68 if self._reportuntrusted:
68 if self._reportuntrusted:
69 self.warn(_('not trusting file %s from untrusted '
69 self.warn(_('not trusting file %s from untrusted '
70 'user %s, group %s\n') % (f, user, group))
70 'user %s, group %s\n') % (f, user, group))
71 return False
71 return False
72
72
73 def readconfig(self, filename, root=None, trust=False,
73 def readconfig(self, filename, root=None, trust=False,
74 sections=None, remap=None):
74 sections=None, remap=None):
75 try:
75 try:
76 fp = open(filename)
76 fp = open(filename)
77 except IOError:
77 except IOError:
78 if not sections: # ignore unless we were looking for something
78 if not sections: # ignore unless we were looking for something
79 return
79 return
80 raise
80 raise
81
81
82 cfg = config.config()
82 cfg = config.config()
83 trusted = sections or trust or self._trusted(fp, filename)
83 trusted = sections or trust or self._trusted(fp, filename)
84
84
85 try:
85 try:
86 cfg.read(filename, fp, sections=sections, remap=remap)
86 cfg.read(filename, fp, sections=sections, remap=remap)
87 fp.close()
87 fp.close()
88 except error.ConfigError, inst:
88 except error.ConfigError, inst:
89 if trusted:
89 if trusted:
90 raise
90 raise
91 self.warn(_("ignored: %s\n") % str(inst))
91 self.warn(_("ignored: %s\n") % str(inst))
92
92
93 if self.plain():
93 if self.plain():
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
95 'logtemplate', 'style',
95 'logtemplate', 'style',
96 'traceback', 'verbose'):
96 'traceback', 'verbose'):
97 if k in cfg['ui']:
97 if k in cfg['ui']:
98 del cfg['ui'][k]
98 del cfg['ui'][k]
99 for k, v in cfg.items('defaults'):
99 for k, v in cfg.items('defaults'):
100 del cfg['defaults'][k]
100 del cfg['defaults'][k]
101 # Don't remove aliases from the configuration if in the exceptionlist
101 # Don't remove aliases from the configuration if in the exceptionlist
102 if self.plain('alias'):
102 if self.plain('alias'):
103 for k, v in cfg.items('alias'):
103 for k, v in cfg.items('alias'):
104 del cfg['alias'][k]
104 del cfg['alias'][k]
105
105
106 if trusted:
106 if trusted:
107 self._tcfg.update(cfg)
107 self._tcfg.update(cfg)
108 self._tcfg.update(self._ocfg)
108 self._tcfg.update(self._ocfg)
109 self._ucfg.update(cfg)
109 self._ucfg.update(cfg)
110 self._ucfg.update(self._ocfg)
110 self._ucfg.update(self._ocfg)
111
111
112 if root is None:
112 if root is None:
113 root = os.path.expanduser('~')
113 root = os.path.expanduser('~')
114 self.fixconfig(root=root)
114 self.fixconfig(root=root)
115
115
116 def fixconfig(self, root=None, section=None):
116 def fixconfig(self, root=None, section=None):
117 if section in (None, 'paths'):
117 if section in (None, 'paths'):
118 # expand vars and ~
118 # expand vars and ~
119 # translate paths relative to root (or home) into absolute paths
119 # translate paths relative to root (or home) into absolute paths
120 root = root or os.getcwd()
120 root = root or os.getcwd()
121 for c in self._tcfg, self._ucfg, self._ocfg:
121 for c in self._tcfg, self._ucfg, self._ocfg:
122 for n, p in c.items('paths'):
122 for n, p in c.items('paths'):
123 if not p:
123 if not p:
124 continue
124 continue
125 if '%%' in p:
125 if '%%' in p:
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
127 % (n, p, self.configsource('paths', n)))
127 % (n, p, self.configsource('paths', n)))
128 p = p.replace('%%', '%')
128 p = p.replace('%%', '%')
129 p = util.expandpath(p)
129 p = util.expandpath(p)
130 if not util.hasscheme(p) and not os.path.isabs(p):
130 if not util.hasscheme(p) and not os.path.isabs(p):
131 p = os.path.normpath(os.path.join(root, p))
131 p = os.path.normpath(os.path.join(root, p))
132 c.set("paths", n, p)
132 c.set("paths", n, p)
133
133
134 if section in (None, 'ui'):
134 if section in (None, 'ui'):
135 # update ui options
135 # update ui options
136 self.debugflag = self.configbool('ui', 'debug')
136 self.debugflag = self.configbool('ui', 'debug')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
139 if self.verbose and self.quiet:
139 if self.verbose and self.quiet:
140 self.quiet = self.verbose = False
140 self.quiet = self.verbose = False
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
141 self._reportuntrusted = self.debugflag or self.configbool("ui",
142 "report_untrusted", True)
142 "report_untrusted", True)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
143 self.tracebackflag = self.configbool('ui', 'traceback', False)
144
144
145 if section in (None, 'trusted'):
145 if section in (None, 'trusted'):
146 # update trust information
146 # update trust information
147 self._trustusers.update(self.configlist('trusted', 'users'))
147 self._trustusers.update(self.configlist('trusted', 'users'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
148 self._trustgroups.update(self.configlist('trusted', 'groups'))
149
149
150 def backupconfig(self, section, item):
150 def backupconfig(self, section, item):
151 return (self._ocfg.backup(section, item),
151 return (self._ocfg.backup(section, item),
152 self._tcfg.backup(section, item),
152 self._tcfg.backup(section, item),
153 self._ucfg.backup(section, item),)
153 self._ucfg.backup(section, item),)
154 def restoreconfig(self, data):
154 def restoreconfig(self, data):
155 self._ocfg.restore(data[0])
155 self._ocfg.restore(data[0])
156 self._tcfg.restore(data[1])
156 self._tcfg.restore(data[1])
157 self._ucfg.restore(data[2])
157 self._ucfg.restore(data[2])
158
158
159 def setconfig(self, section, name, value, overlay=True):
159 def setconfig(self, section, name, value, overlay=True):
160 if overlay:
160 if overlay:
161 self._ocfg.set(section, name, value)
161 self._ocfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
162 self._tcfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
163 self._ucfg.set(section, name, value)
164 self.fixconfig(section=section)
164 self.fixconfig(section=section)
165
165
166 def _data(self, untrusted):
166 def _data(self, untrusted):
167 return untrusted and self._ucfg or self._tcfg
167 return untrusted and self._ucfg or self._tcfg
168
168
169 def configsource(self, section, name, untrusted=False):
169 def configsource(self, section, name, untrusted=False):
170 return self._data(untrusted).source(section, name) or 'none'
170 return self._data(untrusted).source(section, name) or 'none'
171
171
172 def config(self, section, name, default=None, untrusted=False):
172 def config(self, section, name, default=None, untrusted=False):
173 if isinstance(name, list):
173 if isinstance(name, list):
174 alternates = name
174 alternates = name
175 else:
175 else:
176 alternates = [name]
176 alternates = [name]
177
177
178 for n in alternates:
178 for n in alternates:
179 value = self._data(untrusted).get(section, name, None)
179 value = self._data(untrusted).get(section, name, None)
180 if value is not None:
180 if value is not None:
181 name = n
181 name = n
182 break
182 break
183 else:
183 else:
184 value = default
184 value = default
185
185
186 if self.debugflag and not untrusted and self._reportuntrusted:
186 if self.debugflag and not untrusted and self._reportuntrusted:
187 uvalue = self._ucfg.get(section, name)
187 uvalue = self._ucfg.get(section, name)
188 if uvalue is not None and uvalue != value:
188 if uvalue is not None and uvalue != value:
189 self.debug("ignoring untrusted configuration option "
189 self.debug("ignoring untrusted configuration option "
190 "%s.%s = %s\n" % (section, name, uvalue))
190 "%s.%s = %s\n" % (section, name, uvalue))
191 return value
191 return value
192
192
193 def configpath(self, section, name, default=None, untrusted=False):
193 def configpath(self, section, name, default=None, untrusted=False):
194 'get a path config item, expanded relative to repo root or config file'
194 'get a path config item, expanded relative to repo root or config file'
195 v = self.config(section, name, default, untrusted)
195 v = self.config(section, name, default, untrusted)
196 if v is None:
196 if v is None:
197 return None
197 return None
198 if not os.path.isabs(v) or "://" not in v:
198 if not os.path.isabs(v) or "://" not in v:
199 src = self.configsource(section, name, untrusted)
199 src = self.configsource(section, name, untrusted)
200 if ':' in src:
200 if ':' in src:
201 base = os.path.dirname(src.rsplit(':')[0])
201 base = os.path.dirname(src.rsplit(':')[0])
202 v = os.path.join(base, os.path.expanduser(v))
202 v = os.path.join(base, os.path.expanduser(v))
203 return v
203 return v
204
204
205 def configbool(self, section, name, default=False, untrusted=False):
205 def configbool(self, section, name, default=False, untrusted=False):
206 """parse a configuration element as a boolean
206 """parse a configuration element as a boolean
207
207
208 >>> u = ui(); s = 'foo'
208 >>> u = ui(); s = 'foo'
209 >>> u.setconfig(s, 'true', 'yes')
209 >>> u.setconfig(s, 'true', 'yes')
210 >>> u.configbool(s, 'true')
210 >>> u.configbool(s, 'true')
211 True
211 True
212 >>> u.setconfig(s, 'false', 'no')
212 >>> u.setconfig(s, 'false', 'no')
213 >>> u.configbool(s, 'false')
213 >>> u.configbool(s, 'false')
214 False
214 False
215 >>> u.configbool(s, 'unknown')
215 >>> u.configbool(s, 'unknown')
216 False
216 False
217 >>> u.configbool(s, 'unknown', True)
217 >>> u.configbool(s, 'unknown', True)
218 True
218 True
219 >>> u.setconfig(s, 'invalid', 'somevalue')
219 >>> u.setconfig(s, 'invalid', 'somevalue')
220 >>> u.configbool(s, 'invalid')
220 >>> u.configbool(s, 'invalid')
221 Traceback (most recent call last):
221 Traceback (most recent call last):
222 ...
222 ...
223 ConfigError: foo.invalid is not a boolean ('somevalue')
223 ConfigError: foo.invalid is not a boolean ('somevalue')
224 """
224 """
225
225
226 v = self.config(section, name, None, untrusted)
226 v = self.config(section, name, None, untrusted)
227 if v is None:
227 if v is None:
228 return default
228 return default
229 if isinstance(v, bool):
229 if isinstance(v, bool):
230 return v
230 return v
231 b = util.parsebool(v)
231 b = util.parsebool(v)
232 if b is None:
232 if b is None:
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
234 % (section, name, v))
234 % (section, name, v))
235 return b
235 return b
236
236
237 def configint(self, section, name, default=None, untrusted=False):
237 def configint(self, section, name, default=None, untrusted=False):
238 """parse a configuration element as an integer
238 """parse a configuration element as an integer
239
239
240 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
241 >>> u.setconfig(s, 'int1', '42')
241 >>> u.setconfig(s, 'int1', '42')
242 >>> u.configint(s, 'int1')
242 >>> u.configint(s, 'int1')
243 42
243 42
244 >>> u.setconfig(s, 'int2', '-42')
244 >>> u.setconfig(s, 'int2', '-42')
245 >>> u.configint(s, 'int2')
245 >>> u.configint(s, 'int2')
246 -42
246 -42
247 >>> u.configint(s, 'unknown', 7)
247 >>> u.configint(s, 'unknown', 7)
248 7
248 7
249 >>> u.setconfig(s, 'invalid', 'somevalue')
249 >>> u.setconfig(s, 'invalid', 'somevalue')
250 >>> u.configint(s, 'invalid')
250 >>> u.configint(s, 'invalid')
251 Traceback (most recent call last):
251 Traceback (most recent call last):
252 ...
252 ...
253 ConfigError: foo.invalid is not an integer ('somevalue')
253 ConfigError: foo.invalid is not an integer ('somevalue')
254 """
254 """
255
255
256 v = self.config(section, name, None, untrusted)
256 v = self.config(section, name, None, untrusted)
257 if v is None:
257 if v is None:
258 return default
258 return default
259 try:
259 try:
260 return int(v)
260 return int(v)
261 except ValueError:
261 except ValueError:
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
263 % (section, name, v))
263 % (section, name, v))
264
264
265 def configbytes(self, section, name, default=0, untrusted=False):
266 """parse a configuration element as a quantity in bytes
267
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
269 mb (megabytes), g or gb (gigabytes).
270
271 >>> u = ui(); s = 'foo'
272 >>> u.setconfig(s, 'val1', '42')
273 >>> u.configbytes(s, 'val1')
274 42
275 >>> u.setconfig(s, 'val2', '42.5 kb')
276 >>> u.configbytes(s, 'val2')
277 43520
278 >>> u.configbytes(s, 'unknown', '7 MB')
279 7340032
280 >>> u.setconfig(s, 'invalid', 'somevalue')
281 >>> u.configbytes(s, 'invalid')
282 Traceback (most recent call last):
283 ...
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
285 """
286
287 orig = string = self.config(section, name)
288 if orig is None:
289 if not isinstance(default, str):
290 return default
291 orig = string = default
292 multiple = 1
293 m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I)
294 if m:
295 string, key = m.groups()
296 key = key.lower()
297 multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1)
298 try:
299 return int(float(string) * multiple)
300 except ValueError:
301 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
302 % (section, name, orig))
303
265 def configlist(self, section, name, default=None, untrusted=False):
304 def configlist(self, section, name, default=None, untrusted=False):
266 """parse a configuration element as a list of comma/space separated
305 """parse a configuration element as a list of comma/space separated
267 strings
306 strings
268
307
269 >>> u = ui(); s = 'foo'
308 >>> u = ui(); s = 'foo'
270 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
309 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
271 >>> u.configlist(s, 'list1')
310 >>> u.configlist(s, 'list1')
272 ['this', 'is', 'a small', 'test']
311 ['this', 'is', 'a small', 'test']
273 """
312 """
274
313
275 def _parse_plain(parts, s, offset):
314 def _parse_plain(parts, s, offset):
276 whitespace = False
315 whitespace = False
277 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
316 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
278 whitespace = True
317 whitespace = True
279 offset += 1
318 offset += 1
280 if offset >= len(s):
319 if offset >= len(s):
281 return None, parts, offset
320 return None, parts, offset
282 if whitespace:
321 if whitespace:
283 parts.append('')
322 parts.append('')
284 if s[offset] == '"' and not parts[-1]:
323 if s[offset] == '"' and not parts[-1]:
285 return _parse_quote, parts, offset + 1
324 return _parse_quote, parts, offset + 1
286 elif s[offset] == '"' and parts[-1][-1] == '\\':
325 elif s[offset] == '"' and parts[-1][-1] == '\\':
287 parts[-1] = parts[-1][:-1] + s[offset]
326 parts[-1] = parts[-1][:-1] + s[offset]
288 return _parse_plain, parts, offset + 1
327 return _parse_plain, parts, offset + 1
289 parts[-1] += s[offset]
328 parts[-1] += s[offset]
290 return _parse_plain, parts, offset + 1
329 return _parse_plain, parts, offset + 1
291
330
292 def _parse_quote(parts, s, offset):
331 def _parse_quote(parts, s, offset):
293 if offset < len(s) and s[offset] == '"': # ""
332 if offset < len(s) and s[offset] == '"': # ""
294 parts.append('')
333 parts.append('')
295 offset += 1
334 offset += 1
296 while offset < len(s) and (s[offset].isspace() or
335 while offset < len(s) and (s[offset].isspace() or
297 s[offset] == ','):
336 s[offset] == ','):
298 offset += 1
337 offset += 1
299 return _parse_plain, parts, offset
338 return _parse_plain, parts, offset
300
339
301 while offset < len(s) and s[offset] != '"':
340 while offset < len(s) and s[offset] != '"':
302 if (s[offset] == '\\' and offset + 1 < len(s)
341 if (s[offset] == '\\' and offset + 1 < len(s)
303 and s[offset + 1] == '"'):
342 and s[offset + 1] == '"'):
304 offset += 1
343 offset += 1
305 parts[-1] += '"'
344 parts[-1] += '"'
306 else:
345 else:
307 parts[-1] += s[offset]
346 parts[-1] += s[offset]
308 offset += 1
347 offset += 1
309
348
310 if offset >= len(s):
349 if offset >= len(s):
311 real_parts = _configlist(parts[-1])
350 real_parts = _configlist(parts[-1])
312 if not real_parts:
351 if not real_parts:
313 parts[-1] = '"'
352 parts[-1] = '"'
314 else:
353 else:
315 real_parts[0] = '"' + real_parts[0]
354 real_parts[0] = '"' + real_parts[0]
316 parts = parts[:-1]
355 parts = parts[:-1]
317 parts.extend(real_parts)
356 parts.extend(real_parts)
318 return None, parts, offset
357 return None, parts, offset
319
358
320 offset += 1
359 offset += 1
321 while offset < len(s) and s[offset] in [' ', ',']:
360 while offset < len(s) and s[offset] in [' ', ',']:
322 offset += 1
361 offset += 1
323
362
324 if offset < len(s):
363 if offset < len(s):
325 if offset + 1 == len(s) and s[offset] == '"':
364 if offset + 1 == len(s) and s[offset] == '"':
326 parts[-1] += '"'
365 parts[-1] += '"'
327 offset += 1
366 offset += 1
328 else:
367 else:
329 parts.append('')
368 parts.append('')
330 else:
369 else:
331 return None, parts, offset
370 return None, parts, offset
332
371
333 return _parse_plain, parts, offset
372 return _parse_plain, parts, offset
334
373
335 def _configlist(s):
374 def _configlist(s):
336 s = s.rstrip(' ,')
375 s = s.rstrip(' ,')
337 if not s:
376 if not s:
338 return []
377 return []
339 parser, parts, offset = _parse_plain, [''], 0
378 parser, parts, offset = _parse_plain, [''], 0
340 while parser:
379 while parser:
341 parser, parts, offset = parser(parts, s, offset)
380 parser, parts, offset = parser(parts, s, offset)
342 return parts
381 return parts
343
382
344 result = self.config(section, name, untrusted=untrusted)
383 result = self.config(section, name, untrusted=untrusted)
345 if result is None:
384 if result is None:
346 result = default or []
385 result = default or []
347 if isinstance(result, basestring):
386 if isinstance(result, basestring):
348 result = _configlist(result.lstrip(' ,\n'))
387 result = _configlist(result.lstrip(' ,\n'))
349 if result is None:
388 if result is None:
350 result = default or []
389 result = default or []
351 return result
390 return result
352
391
353 def has_section(self, section, untrusted=False):
392 def has_section(self, section, untrusted=False):
354 '''tell whether section exists in config.'''
393 '''tell whether section exists in config.'''
355 return section in self._data(untrusted)
394 return section in self._data(untrusted)
356
395
357 def configitems(self, section, untrusted=False):
396 def configitems(self, section, untrusted=False):
358 items = self._data(untrusted).items(section)
397 items = self._data(untrusted).items(section)
359 if self.debugflag and not untrusted and self._reportuntrusted:
398 if self.debugflag and not untrusted and self._reportuntrusted:
360 for k, v in self._ucfg.items(section):
399 for k, v in self._ucfg.items(section):
361 if self._tcfg.get(section, k) != v:
400 if self._tcfg.get(section, k) != v:
362 self.debug("ignoring untrusted configuration option "
401 self.debug("ignoring untrusted configuration option "
363 "%s.%s = %s\n" % (section, k, v))
402 "%s.%s = %s\n" % (section, k, v))
364 return items
403 return items
365
404
366 def walkconfig(self, untrusted=False):
405 def walkconfig(self, untrusted=False):
367 cfg = self._data(untrusted)
406 cfg = self._data(untrusted)
368 for section in cfg.sections():
407 for section in cfg.sections():
369 for name, value in self.configitems(section, untrusted):
408 for name, value in self.configitems(section, untrusted):
370 yield section, name, value
409 yield section, name, value
371
410
372 def plain(self, feature=None):
411 def plain(self, feature=None):
373 '''is plain mode active?
412 '''is plain mode active?
374
413
375 Plain mode means that all configuration variables which affect
414 Plain mode means that all configuration variables which affect
376 the behavior and output of Mercurial should be
415 the behavior and output of Mercurial should be
377 ignored. Additionally, the output should be stable,
416 ignored. Additionally, the output should be stable,
378 reproducible and suitable for use in scripts or applications.
417 reproducible and suitable for use in scripts or applications.
379
418
380 The only way to trigger plain mode is by setting either the
419 The only way to trigger plain mode is by setting either the
381 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
420 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
382
421
383 The return value can either be
422 The return value can either be
384 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
423 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
385 - True otherwise
424 - True otherwise
386 '''
425 '''
387 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
426 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
388 return False
427 return False
389 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
428 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
390 if feature and exceptions:
429 if feature and exceptions:
391 return feature not in exceptions
430 return feature not in exceptions
392 return True
431 return True
393
432
394 def username(self):
433 def username(self):
395 """Return default username to be used in commits.
434 """Return default username to be used in commits.
396
435
397 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
436 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
398 and stop searching if one of these is set.
437 and stop searching if one of these is set.
399 If not found and ui.askusername is True, ask the user, else use
438 If not found and ui.askusername is True, ask the user, else use
400 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
439 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
401 """
440 """
402 user = os.environ.get("HGUSER")
441 user = os.environ.get("HGUSER")
403 if user is None:
442 if user is None:
404 user = self.config("ui", "username")
443 user = self.config("ui", "username")
405 if user is not None:
444 if user is not None:
406 user = os.path.expandvars(user)
445 user = os.path.expandvars(user)
407 if user is None:
446 if user is None:
408 user = os.environ.get("EMAIL")
447 user = os.environ.get("EMAIL")
409 if user is None and self.configbool("ui", "askusername"):
448 if user is None and self.configbool("ui", "askusername"):
410 user = self.prompt(_("enter a commit username:"), default=None)
449 user = self.prompt(_("enter a commit username:"), default=None)
411 if user is None and not self.interactive():
450 if user is None and not self.interactive():
412 try:
451 try:
413 user = '%s@%s' % (util.getuser(), socket.getfqdn())
452 user = '%s@%s' % (util.getuser(), socket.getfqdn())
414 self.warn(_("no username found, using '%s' instead\n") % user)
453 self.warn(_("no username found, using '%s' instead\n") % user)
415 except KeyError:
454 except KeyError:
416 pass
455 pass
417 if not user:
456 if not user:
418 raise util.Abort(_('no username supplied (see "hg help config")'))
457 raise util.Abort(_('no username supplied (see "hg help config")'))
419 if "\n" in user:
458 if "\n" in user:
420 raise util.Abort(_("username %s contains a newline\n") % repr(user))
459 raise util.Abort(_("username %s contains a newline\n") % repr(user))
421 return user
460 return user
422
461
423 def shortuser(self, user):
462 def shortuser(self, user):
424 """Return a short representation of a user name or email address."""
463 """Return a short representation of a user name or email address."""
425 if not self.verbose:
464 if not self.verbose:
426 user = util.shortuser(user)
465 user = util.shortuser(user)
427 return user
466 return user
428
467
429 def expandpath(self, loc, default=None):
468 def expandpath(self, loc, default=None):
430 """Return repository location relative to cwd or from [paths]"""
469 """Return repository location relative to cwd or from [paths]"""
431 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
470 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
432 return loc
471 return loc
433
472
434 path = self.config('paths', loc)
473 path = self.config('paths', loc)
435 if not path and default is not None:
474 if not path and default is not None:
436 path = self.config('paths', default)
475 path = self.config('paths', default)
437 return path or loc
476 return path or loc
438
477
439 def pushbuffer(self):
478 def pushbuffer(self):
440 self._buffers.append([])
479 self._buffers.append([])
441
480
442 def popbuffer(self, labeled=False):
481 def popbuffer(self, labeled=False):
443 '''pop the last buffer and return the buffered output
482 '''pop the last buffer and return the buffered output
444
483
445 If labeled is True, any labels associated with buffered
484 If labeled is True, any labels associated with buffered
446 output will be handled. By default, this has no effect
485 output will be handled. By default, this has no effect
447 on the output returned, but extensions and GUI tools may
486 on the output returned, but extensions and GUI tools may
448 handle this argument and returned styled output. If output
487 handle this argument and returned styled output. If output
449 is being buffered so it can be captured and parsed or
488 is being buffered so it can be captured and parsed or
450 processed, labeled should not be set to True.
489 processed, labeled should not be set to True.
451 '''
490 '''
452 return "".join(self._buffers.pop())
491 return "".join(self._buffers.pop())
453
492
454 def write(self, *args, **opts):
493 def write(self, *args, **opts):
455 '''write args to output
494 '''write args to output
456
495
457 By default, this method simply writes to the buffer or stdout,
496 By default, this method simply writes to the buffer or stdout,
458 but extensions or GUI tools may override this method,
497 but extensions or GUI tools may override this method,
459 write_err(), popbuffer(), and label() to style output from
498 write_err(), popbuffer(), and label() to style output from
460 various parts of hg.
499 various parts of hg.
461
500
462 An optional keyword argument, "label", can be passed in.
501 An optional keyword argument, "label", can be passed in.
463 This should be a string containing label names separated by
502 This should be a string containing label names separated by
464 space. Label names take the form of "topic.type". For example,
503 space. Label names take the form of "topic.type". For example,
465 ui.debug() issues a label of "ui.debug".
504 ui.debug() issues a label of "ui.debug".
466
505
467 When labeling output for a specific command, a label of
506 When labeling output for a specific command, a label of
468 "cmdname.type" is recommended. For example, status issues
507 "cmdname.type" is recommended. For example, status issues
469 a label of "status.modified" for modified files.
508 a label of "status.modified" for modified files.
470 '''
509 '''
471 if self._buffers:
510 if self._buffers:
472 self._buffers[-1].extend([str(a) for a in args])
511 self._buffers[-1].extend([str(a) for a in args])
473 else:
512 else:
474 for a in args:
513 for a in args:
475 self.fout.write(str(a))
514 self.fout.write(str(a))
476
515
477 def write_err(self, *args, **opts):
516 def write_err(self, *args, **opts):
478 try:
517 try:
479 if not getattr(self.fout, 'closed', False):
518 if not getattr(self.fout, 'closed', False):
480 self.fout.flush()
519 self.fout.flush()
481 for a in args:
520 for a in args:
482 self.ferr.write(str(a))
521 self.ferr.write(str(a))
483 # stderr may be buffered under win32 when redirected to files,
522 # stderr may be buffered under win32 when redirected to files,
484 # including stdout.
523 # including stdout.
485 if not getattr(self.ferr, 'closed', False):
524 if not getattr(self.ferr, 'closed', False):
486 self.ferr.flush()
525 self.ferr.flush()
487 except IOError, inst:
526 except IOError, inst:
488 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
527 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
489 raise
528 raise
490
529
491 def flush(self):
530 def flush(self):
492 try: self.fout.flush()
531 try: self.fout.flush()
493 except (IOError, ValueError): pass
532 except (IOError, ValueError): pass
494 try: self.ferr.flush()
533 try: self.ferr.flush()
495 except (IOError, ValueError): pass
534 except (IOError, ValueError): pass
496
535
497 def _isatty(self, fh):
536 def _isatty(self, fh):
498 if self.configbool('ui', 'nontty', False):
537 if self.configbool('ui', 'nontty', False):
499 return False
538 return False
500 return util.isatty(fh)
539 return util.isatty(fh)
501
540
502 def interactive(self):
541 def interactive(self):
503 '''is interactive input allowed?
542 '''is interactive input allowed?
504
543
505 An interactive session is a session where input can be reasonably read
544 An interactive session is a session where input can be reasonably read
506 from `sys.stdin'. If this function returns false, any attempt to read
545 from `sys.stdin'. If this function returns false, any attempt to read
507 from stdin should fail with an error, unless a sensible default has been
546 from stdin should fail with an error, unless a sensible default has been
508 specified.
547 specified.
509
548
510 Interactiveness is triggered by the value of the `ui.interactive'
549 Interactiveness is triggered by the value of the `ui.interactive'
511 configuration variable or - if it is unset - when `sys.stdin' points
550 configuration variable or - if it is unset - when `sys.stdin' points
512 to a terminal device.
551 to a terminal device.
513
552
514 This function refers to input only; for output, see `ui.formatted()'.
553 This function refers to input only; for output, see `ui.formatted()'.
515 '''
554 '''
516 i = self.configbool("ui", "interactive", None)
555 i = self.configbool("ui", "interactive", None)
517 if i is None:
556 if i is None:
518 # some environments replace stdin without implementing isatty
557 # some environments replace stdin without implementing isatty
519 # usually those are non-interactive
558 # usually those are non-interactive
520 return self._isatty(self.fin)
559 return self._isatty(self.fin)
521
560
522 return i
561 return i
523
562
524 def termwidth(self):
563 def termwidth(self):
525 '''how wide is the terminal in columns?
564 '''how wide is the terminal in columns?
526 '''
565 '''
527 if 'COLUMNS' in os.environ:
566 if 'COLUMNS' in os.environ:
528 try:
567 try:
529 return int(os.environ['COLUMNS'])
568 return int(os.environ['COLUMNS'])
530 except ValueError:
569 except ValueError:
531 pass
570 pass
532 return util.termwidth()
571 return util.termwidth()
533
572
534 def formatted(self):
573 def formatted(self):
535 '''should formatted output be used?
574 '''should formatted output be used?
536
575
537 It is often desirable to format the output to suite the output medium.
576 It is often desirable to format the output to suite the output medium.
538 Examples of this are truncating long lines or colorizing messages.
577 Examples of this are truncating long lines or colorizing messages.
539 However, this is not often not desirable when piping output into other
578 However, this is not often not desirable when piping output into other
540 utilities, e.g. `grep'.
579 utilities, e.g. `grep'.
541
580
542 Formatted output is triggered by the value of the `ui.formatted'
581 Formatted output is triggered by the value of the `ui.formatted'
543 configuration variable or - if it is unset - when `sys.stdout' points
582 configuration variable or - if it is unset - when `sys.stdout' points
544 to a terminal device. Please note that `ui.formatted' should be
583 to a terminal device. Please note that `ui.formatted' should be
545 considered an implementation detail; it is not intended for use outside
584 considered an implementation detail; it is not intended for use outside
546 Mercurial or its extensions.
585 Mercurial or its extensions.
547
586
548 This function refers to output only; for input, see `ui.interactive()'.
587 This function refers to output only; for input, see `ui.interactive()'.
549 This function always returns false when in plain mode, see `ui.plain()'.
588 This function always returns false when in plain mode, see `ui.plain()'.
550 '''
589 '''
551 if self.plain():
590 if self.plain():
552 return False
591 return False
553
592
554 i = self.configbool("ui", "formatted", None)
593 i = self.configbool("ui", "formatted", None)
555 if i is None:
594 if i is None:
556 # some environments replace stdout without implementing isatty
595 # some environments replace stdout without implementing isatty
557 # usually those are non-interactive
596 # usually those are non-interactive
558 return self._isatty(self.fout)
597 return self._isatty(self.fout)
559
598
560 return i
599 return i
561
600
562 def _readline(self, prompt=''):
601 def _readline(self, prompt=''):
563 if self._isatty(self.fin):
602 if self._isatty(self.fin):
564 try:
603 try:
565 # magically add command line editing support, where
604 # magically add command line editing support, where
566 # available
605 # available
567 import readline
606 import readline
568 # force demandimport to really load the module
607 # force demandimport to really load the module
569 readline.read_history_file
608 readline.read_history_file
570 # windows sometimes raises something other than ImportError
609 # windows sometimes raises something other than ImportError
571 except Exception:
610 except Exception:
572 pass
611 pass
573
612
574 # call write() so output goes through subclassed implementation
613 # call write() so output goes through subclassed implementation
575 # e.g. color extension on Windows
614 # e.g. color extension on Windows
576 self.write(prompt)
615 self.write(prompt)
577
616
578 # instead of trying to emulate raw_input, swap (self.fin,
617 # instead of trying to emulate raw_input, swap (self.fin,
579 # self.fout) with (sys.stdin, sys.stdout)
618 # self.fout) with (sys.stdin, sys.stdout)
580 oldin = sys.stdin
619 oldin = sys.stdin
581 oldout = sys.stdout
620 oldout = sys.stdout
582 sys.stdin = self.fin
621 sys.stdin = self.fin
583 sys.stdout = self.fout
622 sys.stdout = self.fout
584 line = raw_input(' ')
623 line = raw_input(' ')
585 sys.stdin = oldin
624 sys.stdin = oldin
586 sys.stdout = oldout
625 sys.stdout = oldout
587
626
588 # When stdin is in binary mode on Windows, it can cause
627 # When stdin is in binary mode on Windows, it can cause
589 # raw_input() to emit an extra trailing carriage return
628 # raw_input() to emit an extra trailing carriage return
590 if os.linesep == '\r\n' and line and line[-1] == '\r':
629 if os.linesep == '\r\n' and line and line[-1] == '\r':
591 line = line[:-1]
630 line = line[:-1]
592 return line
631 return line
593
632
594 def prompt(self, msg, default="y"):
633 def prompt(self, msg, default="y"):
595 """Prompt user with msg, read response.
634 """Prompt user with msg, read response.
596 If ui is not interactive, the default is returned.
635 If ui is not interactive, the default is returned.
597 """
636 """
598 if not self.interactive():
637 if not self.interactive():
599 self.write(msg, ' ', default, "\n")
638 self.write(msg, ' ', default, "\n")
600 return default
639 return default
601 try:
640 try:
602 r = self._readline(self.label(msg, 'ui.prompt'))
641 r = self._readline(self.label(msg, 'ui.prompt'))
603 if not r:
642 if not r:
604 return default
643 return default
605 return r
644 return r
606 except EOFError:
645 except EOFError:
607 raise util.Abort(_('response expected'))
646 raise util.Abort(_('response expected'))
608
647
609 def promptchoice(self, msg, choices, default=0):
648 def promptchoice(self, msg, choices, default=0):
610 """Prompt user with msg, read response, and ensure it matches
649 """Prompt user with msg, read response, and ensure it matches
611 one of the provided choices. The index of the choice is returned.
650 one of the provided choices. The index of the choice is returned.
612 choices is a sequence of acceptable responses with the format:
651 choices is a sequence of acceptable responses with the format:
613 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
652 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
614 If ui is not interactive, the default is returned.
653 If ui is not interactive, the default is returned.
615 """
654 """
616 resps = [s[s.index('&') + 1].lower() for s in choices]
655 resps = [s[s.index('&') + 1].lower() for s in choices]
617 while True:
656 while True:
618 r = self.prompt(msg, resps[default])
657 r = self.prompt(msg, resps[default])
619 if r.lower() in resps:
658 if r.lower() in resps:
620 return resps.index(r.lower())
659 return resps.index(r.lower())
621 self.write(_("unrecognized response\n"))
660 self.write(_("unrecognized response\n"))
622
661
623 def getpass(self, prompt=None, default=None):
662 def getpass(self, prompt=None, default=None):
624 if not self.interactive():
663 if not self.interactive():
625 return default
664 return default
626 try:
665 try:
627 return getpass.getpass(prompt or _('password: '))
666 return getpass.getpass(prompt or _('password: '))
628 except EOFError:
667 except EOFError:
629 raise util.Abort(_('response expected'))
668 raise util.Abort(_('response expected'))
630 def status(self, *msg, **opts):
669 def status(self, *msg, **opts):
631 '''write status message to output (if ui.quiet is False)
670 '''write status message to output (if ui.quiet is False)
632
671
633 This adds an output label of "ui.status".
672 This adds an output label of "ui.status".
634 '''
673 '''
635 if not self.quiet:
674 if not self.quiet:
636 opts['label'] = opts.get('label', '') + ' ui.status'
675 opts['label'] = opts.get('label', '') + ' ui.status'
637 self.write(*msg, **opts)
676 self.write(*msg, **opts)
638 def warn(self, *msg, **opts):
677 def warn(self, *msg, **opts):
639 '''write warning message to output (stderr)
678 '''write warning message to output (stderr)
640
679
641 This adds an output label of "ui.warning".
680 This adds an output label of "ui.warning".
642 '''
681 '''
643 opts['label'] = opts.get('label', '') + ' ui.warning'
682 opts['label'] = opts.get('label', '') + ' ui.warning'
644 self.write_err(*msg, **opts)
683 self.write_err(*msg, **opts)
645 def note(self, *msg, **opts):
684 def note(self, *msg, **opts):
646 '''write note to output (if ui.verbose is True)
685 '''write note to output (if ui.verbose is True)
647
686
648 This adds an output label of "ui.note".
687 This adds an output label of "ui.note".
649 '''
688 '''
650 if self.verbose:
689 if self.verbose:
651 opts['label'] = opts.get('label', '') + ' ui.note'
690 opts['label'] = opts.get('label', '') + ' ui.note'
652 self.write(*msg, **opts)
691 self.write(*msg, **opts)
653 def debug(self, *msg, **opts):
692 def debug(self, *msg, **opts):
654 '''write debug message to output (if ui.debugflag is True)
693 '''write debug message to output (if ui.debugflag is True)
655
694
656 This adds an output label of "ui.debug".
695 This adds an output label of "ui.debug".
657 '''
696 '''
658 if self.debugflag:
697 if self.debugflag:
659 opts['label'] = opts.get('label', '') + ' ui.debug'
698 opts['label'] = opts.get('label', '') + ' ui.debug'
660 self.write(*msg, **opts)
699 self.write(*msg, **opts)
661 def edit(self, text, user):
700 def edit(self, text, user):
662 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
701 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
663 text=True)
702 text=True)
664 try:
703 try:
665 f = os.fdopen(fd, "w")
704 f = os.fdopen(fd, "w")
666 f.write(text)
705 f.write(text)
667 f.close()
706 f.close()
668
707
669 editor = self.geteditor()
708 editor = self.geteditor()
670
709
671 util.system("%s \"%s\"" % (editor, name),
710 util.system("%s \"%s\"" % (editor, name),
672 environ={'HGUSER': user},
711 environ={'HGUSER': user},
673 onerr=util.Abort, errprefix=_("edit failed"),
712 onerr=util.Abort, errprefix=_("edit failed"),
674 out=self.fout)
713 out=self.fout)
675
714
676 f = open(name)
715 f = open(name)
677 t = f.read()
716 t = f.read()
678 f.close()
717 f.close()
679 finally:
718 finally:
680 os.unlink(name)
719 os.unlink(name)
681
720
682 return t
721 return t
683
722
684 def traceback(self, exc=None, force=False):
723 def traceback(self, exc=None, force=False):
685 '''print exception traceback if traceback printing enabled or forced.
724 '''print exception traceback if traceback printing enabled or forced.
686 only to call in exception handler. returns true if traceback
725 only to call in exception handler. returns true if traceback
687 printed.'''
726 printed.'''
688 if self.tracebackflag or force:
727 if self.tracebackflag or force:
689 if exc is None:
728 if exc is None:
690 exc = sys.exc_info()
729 exc = sys.exc_info()
691 cause = getattr(exc[1], 'cause', None)
730 cause = getattr(exc[1], 'cause', None)
692
731
693 if cause is not None:
732 if cause is not None:
694 causetb = traceback.format_tb(cause[2])
733 causetb = traceback.format_tb(cause[2])
695 exctb = traceback.format_tb(exc[2])
734 exctb = traceback.format_tb(exc[2])
696 exconly = traceback.format_exception_only(cause[0], cause[1])
735 exconly = traceback.format_exception_only(cause[0], cause[1])
697
736
698 # exclude frame where 'exc' was chained and rethrown from exctb
737 # exclude frame where 'exc' was chained and rethrown from exctb
699 self.write_err('Traceback (most recent call last):\n',
738 self.write_err('Traceback (most recent call last):\n',
700 ''.join(exctb[:-1]),
739 ''.join(exctb[:-1]),
701 ''.join(causetb),
740 ''.join(causetb),
702 ''.join(exconly))
741 ''.join(exconly))
703 else:
742 else:
704 traceback.print_exception(exc[0], exc[1], exc[2],
743 traceback.print_exception(exc[0], exc[1], exc[2],
705 file=self.ferr)
744 file=self.ferr)
706 return self.tracebackflag or force
745 return self.tracebackflag or force
707
746
708 def geteditor(self):
747 def geteditor(self):
709 '''return editor to use'''
748 '''return editor to use'''
710 if sys.platform == 'plan9':
749 if sys.platform == 'plan9':
711 # vi is the MIPS instruction simulator on Plan 9. We
750 # vi is the MIPS instruction simulator on Plan 9. We
712 # instead default to E to plumb commit messages to
751 # instead default to E to plumb commit messages to
713 # avoid confusion.
752 # avoid confusion.
714 editor = 'E'
753 editor = 'E'
715 else:
754 else:
716 editor = 'vi'
755 editor = 'vi'
717 return (os.environ.get("HGEDITOR") or
756 return (os.environ.get("HGEDITOR") or
718 self.config("ui", "editor") or
757 self.config("ui", "editor") or
719 os.environ.get("VISUAL") or
758 os.environ.get("VISUAL") or
720 os.environ.get("EDITOR", editor))
759 os.environ.get("EDITOR", editor))
721
760
722 def progress(self, topic, pos, item="", unit="", total=None):
761 def progress(self, topic, pos, item="", unit="", total=None):
723 '''show a progress message
762 '''show a progress message
724
763
725 With stock hg, this is simply a debug message that is hidden
764 With stock hg, this is simply a debug message that is hidden
726 by default, but with extensions or GUI tools it may be
765 by default, but with extensions or GUI tools it may be
727 visible. 'topic' is the current operation, 'item' is a
766 visible. 'topic' is the current operation, 'item' is a
728 non-numeric marker of the current position (i.e. the currently
767 non-numeric marker of the current position (i.e. the currently
729 in-process file), 'pos' is the current numeric position (i.e.
768 in-process file), 'pos' is the current numeric position (i.e.
730 revision, bytes, etc.), unit is a corresponding unit label,
769 revision, bytes, etc.), unit is a corresponding unit label,
731 and total is the highest expected pos.
770 and total is the highest expected pos.
732
771
733 Multiple nested topics may be active at a time.
772 Multiple nested topics may be active at a time.
734
773
735 All topics should be marked closed by setting pos to None at
774 All topics should be marked closed by setting pos to None at
736 termination.
775 termination.
737 '''
776 '''
738
777
739 if pos is None or not self.debugflag:
778 if pos is None or not self.debugflag:
740 return
779 return
741
780
742 if unit:
781 if unit:
743 unit = ' ' + unit
782 unit = ' ' + unit
744 if item:
783 if item:
745 item = ' ' + item
784 item = ' ' + item
746
785
747 if total:
786 if total:
748 pct = 100.0 * pos / total
787 pct = 100.0 * pos / total
749 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
788 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
750 % (topic, item, pos, total, unit, pct))
789 % (topic, item, pos, total, unit, pct))
751 else:
790 else:
752 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
791 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
753
792
754 def log(self, service, *msg, **opts):
793 def log(self, service, *msg, **opts):
755 '''hook for logging facility extensions
794 '''hook for logging facility extensions
756
795
757 service should be a readily-identifiable subsystem, which will
796 service should be a readily-identifiable subsystem, which will
758 allow filtering.
797 allow filtering.
759 message should be a newline-terminated string to log.
798 message should be a newline-terminated string to log.
760 '''
799 '''
761 pass
800 pass
762
801
763 def label(self, msg, label):
802 def label(self, msg, label):
764 '''style msg based on supplied label
803 '''style msg based on supplied label
765
804
766 Like ui.write(), this just returns msg unchanged, but extensions
805 Like ui.write(), this just returns msg unchanged, but extensions
767 and GUI tools can override it to allow styling output without
806 and GUI tools can override it to allow styling output without
768 writing it.
807 writing it.
769
808
770 ui.write(s, 'label') is equivalent to
809 ui.write(s, 'label') is equivalent to
771 ui.write(ui.label(s, 'label')).
810 ui.write(ui.label(s, 'label')).
772 '''
811 '''
773 return msg
812 return msg
General Comments 0
You need to be logged in to leave comments. Login now