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