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