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