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