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