##// END OF EJS Templates
ui: send traceback of devel warning to appropriate output stream...
Yuya Nishihara -
r26451:c8f42c19 default
parent child Browse files
Show More
@@ -1,1123 +1,1123
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import getpass
11 import getpass
12 import inspect
12 import inspect
13 import os
13 import os
14 import socket
14 import socket
15 import sys
15 import sys
16 import tempfile
16 import tempfile
17 import traceback
17 import traceback
18
18
19 from .i18n import _
19 from .i18n import _
20 from .node import hex
20 from .node import hex
21
21
22 from . import (
22 from . import (
23 config,
23 config,
24 error,
24 error,
25 formatter,
25 formatter,
26 progress,
26 progress,
27 scmutil,
27 scmutil,
28 util,
28 util,
29 )
29 )
30
30
31 samplehgrcs = {
31 samplehgrcs = {
32 'user':
32 'user':
33 """# example user config (see "hg help config" for more info)
33 """# example user config (see "hg help config" for more info)
34 [ui]
34 [ui]
35 # name and email, e.g.
35 # name and email, e.g.
36 # username = Jane Doe <jdoe@example.com>
36 # username = Jane Doe <jdoe@example.com>
37 username =
37 username =
38
38
39 [extensions]
39 [extensions]
40 # uncomment these lines to enable some popular extensions
40 # uncomment these lines to enable some popular extensions
41 # (see "hg help extensions" for more info)
41 # (see "hg help extensions" for more info)
42 #
42 #
43 # pager =
43 # pager =
44 # progress =
44 # progress =
45 # color =""",
45 # color =""",
46
46
47 'cloned':
47 'cloned':
48 """# example repository config (see "hg help config" for more info)
48 """# example repository config (see "hg help config" for more info)
49 [paths]
49 [paths]
50 default = %s
50 default = %s
51
51
52 # path aliases to other clones of this repo in URLs or filesystem paths
52 # path aliases to other clones of this repo in URLs or filesystem paths
53 # (see "hg help config.paths" for more info)
53 # (see "hg help config.paths" for more info)
54 #
54 #
55 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
55 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
56 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
56 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
57 # my-clone = /home/jdoe/jdoes-clone
57 # my-clone = /home/jdoe/jdoes-clone
58
58
59 [ui]
59 [ui]
60 # name and email (local to this repository, optional), e.g.
60 # name and email (local to this repository, optional), e.g.
61 # username = Jane Doe <jdoe@example.com>
61 # username = Jane Doe <jdoe@example.com>
62 """,
62 """,
63
63
64 'local':
64 'local':
65 """# example repository config (see "hg help config" for more info)
65 """# example repository config (see "hg help config" for more info)
66 [paths]
66 [paths]
67 # path aliases to other clones of this repo in URLs or filesystem paths
67 # path aliases to other clones of this repo in URLs or filesystem paths
68 # (see "hg help config.paths" for more info)
68 # (see "hg help config.paths" for more info)
69 #
69 #
70 # default = http://example.com/hg/example-repo
70 # default = http://example.com/hg/example-repo
71 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
71 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
72 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
72 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-clone = /home/jdoe/jdoes-clone
73 # my-clone = /home/jdoe/jdoes-clone
74
74
75 [ui]
75 [ui]
76 # name and email (local to this repository, optional), e.g.
76 # name and email (local to this repository, optional), e.g.
77 # username = Jane Doe <jdoe@example.com>
77 # username = Jane Doe <jdoe@example.com>
78 """,
78 """,
79
79
80 'global':
80 'global':
81 """# example system-wide hg config (see "hg help config" for more info)
81 """# example system-wide hg config (see "hg help config" for more info)
82
82
83 [extensions]
83 [extensions]
84 # uncomment these lines to enable some popular extensions
84 # uncomment these lines to enable some popular extensions
85 # (see "hg help extensions" for more info)
85 # (see "hg help extensions" for more info)
86 #
86 #
87 # blackbox =
87 # blackbox =
88 # progress =
88 # progress =
89 # color =
89 # color =
90 # pager =""",
90 # pager =""",
91 }
91 }
92
92
93 class ui(object):
93 class ui(object):
94 def __init__(self, src=None):
94 def __init__(self, src=None):
95 # _buffers: used for temporary capture of output
95 # _buffers: used for temporary capture of output
96 self._buffers = []
96 self._buffers = []
97 # _bufferstates:
97 # _bufferstates:
98 # should the temporary capture include stderr and subprocess output
98 # should the temporary capture include stderr and subprocess output
99 self._bufferstates = []
99 self._bufferstates = []
100 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
100 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
101 self._reportuntrusted = True
101 self._reportuntrusted = True
102 self._ocfg = config.config() # overlay
102 self._ocfg = config.config() # overlay
103 self._tcfg = config.config() # trusted
103 self._tcfg = config.config() # trusted
104 self._ucfg = config.config() # untrusted
104 self._ucfg = config.config() # untrusted
105 self._trustusers = set()
105 self._trustusers = set()
106 self._trustgroups = set()
106 self._trustgroups = set()
107 self.callhooks = True
107 self.callhooks = True
108
108
109 if src:
109 if src:
110 self.fout = src.fout
110 self.fout = src.fout
111 self.ferr = src.ferr
111 self.ferr = src.ferr
112 self.fin = src.fin
112 self.fin = src.fin
113
113
114 self._tcfg = src._tcfg.copy()
114 self._tcfg = src._tcfg.copy()
115 self._ucfg = src._ucfg.copy()
115 self._ucfg = src._ucfg.copy()
116 self._ocfg = src._ocfg.copy()
116 self._ocfg = src._ocfg.copy()
117 self._trustusers = src._trustusers.copy()
117 self._trustusers = src._trustusers.copy()
118 self._trustgroups = src._trustgroups.copy()
118 self._trustgroups = src._trustgroups.copy()
119 self.environ = src.environ
119 self.environ = src.environ
120 self.callhooks = src.callhooks
120 self.callhooks = src.callhooks
121 self.fixconfig()
121 self.fixconfig()
122 else:
122 else:
123 self.fout = sys.stdout
123 self.fout = sys.stdout
124 self.ferr = sys.stderr
124 self.ferr = sys.stderr
125 self.fin = sys.stdin
125 self.fin = sys.stdin
126
126
127 # shared read-only environment
127 # shared read-only environment
128 self.environ = os.environ
128 self.environ = os.environ
129 # we always trust global config files
129 # we always trust global config files
130 for f in scmutil.rcpath():
130 for f in scmutil.rcpath():
131 self.readconfig(f, trust=True)
131 self.readconfig(f, trust=True)
132
132
133 def copy(self):
133 def copy(self):
134 return self.__class__(self)
134 return self.__class__(self)
135
135
136 def formatter(self, topic, opts):
136 def formatter(self, topic, opts):
137 return formatter.formatter(self, topic, opts)
137 return formatter.formatter(self, topic, opts)
138
138
139 def _trusted(self, fp, f):
139 def _trusted(self, fp, f):
140 st = util.fstat(fp)
140 st = util.fstat(fp)
141 if util.isowner(st):
141 if util.isowner(st):
142 return True
142 return True
143
143
144 tusers, tgroups = self._trustusers, self._trustgroups
144 tusers, tgroups = self._trustusers, self._trustgroups
145 if '*' in tusers or '*' in tgroups:
145 if '*' in tusers or '*' in tgroups:
146 return True
146 return True
147
147
148 user = util.username(st.st_uid)
148 user = util.username(st.st_uid)
149 group = util.groupname(st.st_gid)
149 group = util.groupname(st.st_gid)
150 if user in tusers or group in tgroups or user == util.username():
150 if user in tusers or group in tgroups or user == util.username():
151 return True
151 return True
152
152
153 if self._reportuntrusted:
153 if self._reportuntrusted:
154 self.warn(_('not trusting file %s from untrusted '
154 self.warn(_('not trusting file %s from untrusted '
155 'user %s, group %s\n') % (f, user, group))
155 'user %s, group %s\n') % (f, user, group))
156 return False
156 return False
157
157
158 def readconfig(self, filename, root=None, trust=False,
158 def readconfig(self, filename, root=None, trust=False,
159 sections=None, remap=None):
159 sections=None, remap=None):
160 try:
160 try:
161 fp = open(filename)
161 fp = open(filename)
162 except IOError:
162 except IOError:
163 if not sections: # ignore unless we were looking for something
163 if not sections: # ignore unless we were looking for something
164 return
164 return
165 raise
165 raise
166
166
167 cfg = config.config()
167 cfg = config.config()
168 trusted = sections or trust or self._trusted(fp, filename)
168 trusted = sections or trust or self._trusted(fp, filename)
169
169
170 try:
170 try:
171 cfg.read(filename, fp, sections=sections, remap=remap)
171 cfg.read(filename, fp, sections=sections, remap=remap)
172 fp.close()
172 fp.close()
173 except error.ConfigError as inst:
173 except error.ConfigError as inst:
174 if trusted:
174 if trusted:
175 raise
175 raise
176 self.warn(_("ignored: %s\n") % str(inst))
176 self.warn(_("ignored: %s\n") % str(inst))
177
177
178 if self.plain():
178 if self.plain():
179 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
179 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
180 'logtemplate', 'statuscopies', 'style',
180 'logtemplate', 'statuscopies', 'style',
181 'traceback', 'verbose'):
181 'traceback', 'verbose'):
182 if k in cfg['ui']:
182 if k in cfg['ui']:
183 del cfg['ui'][k]
183 del cfg['ui'][k]
184 for k, v in cfg.items('defaults'):
184 for k, v in cfg.items('defaults'):
185 del cfg['defaults'][k]
185 del cfg['defaults'][k]
186 # Don't remove aliases from the configuration if in the exceptionlist
186 # Don't remove aliases from the configuration if in the exceptionlist
187 if self.plain('alias'):
187 if self.plain('alias'):
188 for k, v in cfg.items('alias'):
188 for k, v in cfg.items('alias'):
189 del cfg['alias'][k]
189 del cfg['alias'][k]
190 if self.plain('revsetalias'):
190 if self.plain('revsetalias'):
191 for k, v in cfg.items('revsetalias'):
191 for k, v in cfg.items('revsetalias'):
192 del cfg['revsetalias'][k]
192 del cfg['revsetalias'][k]
193
193
194 if trusted:
194 if trusted:
195 self._tcfg.update(cfg)
195 self._tcfg.update(cfg)
196 self._tcfg.update(self._ocfg)
196 self._tcfg.update(self._ocfg)
197 self._ucfg.update(cfg)
197 self._ucfg.update(cfg)
198 self._ucfg.update(self._ocfg)
198 self._ucfg.update(self._ocfg)
199
199
200 if root is None:
200 if root is None:
201 root = os.path.expanduser('~')
201 root = os.path.expanduser('~')
202 self.fixconfig(root=root)
202 self.fixconfig(root=root)
203
203
204 def fixconfig(self, root=None, section=None):
204 def fixconfig(self, root=None, section=None):
205 if section in (None, 'paths'):
205 if section in (None, 'paths'):
206 # expand vars and ~
206 # expand vars and ~
207 # translate paths relative to root (or home) into absolute paths
207 # translate paths relative to root (or home) into absolute paths
208 root = root or os.getcwd()
208 root = root or os.getcwd()
209 for c in self._tcfg, self._ucfg, self._ocfg:
209 for c in self._tcfg, self._ucfg, self._ocfg:
210 for n, p in c.items('paths'):
210 for n, p in c.items('paths'):
211 if not p:
211 if not p:
212 continue
212 continue
213 if '%%' in p:
213 if '%%' in p:
214 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
214 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
215 % (n, p, self.configsource('paths', n)))
215 % (n, p, self.configsource('paths', n)))
216 p = p.replace('%%', '%')
216 p = p.replace('%%', '%')
217 p = util.expandpath(p)
217 p = util.expandpath(p)
218 if not util.hasscheme(p) and not os.path.isabs(p):
218 if not util.hasscheme(p) and not os.path.isabs(p):
219 p = os.path.normpath(os.path.join(root, p))
219 p = os.path.normpath(os.path.join(root, p))
220 c.set("paths", n, p)
220 c.set("paths", n, p)
221
221
222 if section in (None, 'ui'):
222 if section in (None, 'ui'):
223 # update ui options
223 # update ui options
224 self.debugflag = self.configbool('ui', 'debug')
224 self.debugflag = self.configbool('ui', 'debug')
225 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
225 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
226 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
226 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
227 if self.verbose and self.quiet:
227 if self.verbose and self.quiet:
228 self.quiet = self.verbose = False
228 self.quiet = self.verbose = False
229 self._reportuntrusted = self.debugflag or self.configbool("ui",
229 self._reportuntrusted = self.debugflag or self.configbool("ui",
230 "report_untrusted", True)
230 "report_untrusted", True)
231 self.tracebackflag = self.configbool('ui', 'traceback', False)
231 self.tracebackflag = self.configbool('ui', 'traceback', False)
232
232
233 if section in (None, 'trusted'):
233 if section in (None, 'trusted'):
234 # update trust information
234 # update trust information
235 self._trustusers.update(self.configlist('trusted', 'users'))
235 self._trustusers.update(self.configlist('trusted', 'users'))
236 self._trustgroups.update(self.configlist('trusted', 'groups'))
236 self._trustgroups.update(self.configlist('trusted', 'groups'))
237
237
238 def backupconfig(self, section, item):
238 def backupconfig(self, section, item):
239 return (self._ocfg.backup(section, item),
239 return (self._ocfg.backup(section, item),
240 self._tcfg.backup(section, item),
240 self._tcfg.backup(section, item),
241 self._ucfg.backup(section, item),)
241 self._ucfg.backup(section, item),)
242 def restoreconfig(self, data):
242 def restoreconfig(self, data):
243 self._ocfg.restore(data[0])
243 self._ocfg.restore(data[0])
244 self._tcfg.restore(data[1])
244 self._tcfg.restore(data[1])
245 self._ucfg.restore(data[2])
245 self._ucfg.restore(data[2])
246
246
247 def setconfig(self, section, name, value, source=''):
247 def setconfig(self, section, name, value, source=''):
248 for cfg in (self._ocfg, self._tcfg, self._ucfg):
248 for cfg in (self._ocfg, self._tcfg, self._ucfg):
249 cfg.set(section, name, value, source)
249 cfg.set(section, name, value, source)
250 self.fixconfig(section=section)
250 self.fixconfig(section=section)
251
251
252 def _data(self, untrusted):
252 def _data(self, untrusted):
253 return untrusted and self._ucfg or self._tcfg
253 return untrusted and self._ucfg or self._tcfg
254
254
255 def configsource(self, section, name, untrusted=False):
255 def configsource(self, section, name, untrusted=False):
256 return self._data(untrusted).source(section, name) or 'none'
256 return self._data(untrusted).source(section, name) or 'none'
257
257
258 def config(self, section, name, default=None, untrusted=False):
258 def config(self, section, name, default=None, untrusted=False):
259 if isinstance(name, list):
259 if isinstance(name, list):
260 alternates = name
260 alternates = name
261 else:
261 else:
262 alternates = [name]
262 alternates = [name]
263
263
264 for n in alternates:
264 for n in alternates:
265 value = self._data(untrusted).get(section, n, None)
265 value = self._data(untrusted).get(section, n, None)
266 if value is not None:
266 if value is not None:
267 name = n
267 name = n
268 break
268 break
269 else:
269 else:
270 value = default
270 value = default
271
271
272 if self.debugflag and not untrusted and self._reportuntrusted:
272 if self.debugflag and not untrusted and self._reportuntrusted:
273 for n in alternates:
273 for n in alternates:
274 uvalue = self._ucfg.get(section, n)
274 uvalue = self._ucfg.get(section, n)
275 if uvalue is not None and uvalue != value:
275 if uvalue is not None and uvalue != value:
276 self.debug("ignoring untrusted configuration option "
276 self.debug("ignoring untrusted configuration option "
277 "%s.%s = %s\n" % (section, n, uvalue))
277 "%s.%s = %s\n" % (section, n, uvalue))
278 return value
278 return value
279
279
280 def configpath(self, section, name, default=None, untrusted=False):
280 def configpath(self, section, name, default=None, untrusted=False):
281 'get a path config item, expanded relative to repo root or config file'
281 'get a path config item, expanded relative to repo root or config file'
282 v = self.config(section, name, default, untrusted)
282 v = self.config(section, name, default, untrusted)
283 if v is None:
283 if v is None:
284 return None
284 return None
285 if not os.path.isabs(v) or "://" not in v:
285 if not os.path.isabs(v) or "://" not in v:
286 src = self.configsource(section, name, untrusted)
286 src = self.configsource(section, name, untrusted)
287 if ':' in src:
287 if ':' in src:
288 base = os.path.dirname(src.rsplit(':')[0])
288 base = os.path.dirname(src.rsplit(':')[0])
289 v = os.path.join(base, os.path.expanduser(v))
289 v = os.path.join(base, os.path.expanduser(v))
290 return v
290 return v
291
291
292 def configbool(self, section, name, default=False, untrusted=False):
292 def configbool(self, section, name, default=False, untrusted=False):
293 """parse a configuration element as a boolean
293 """parse a configuration element as a boolean
294
294
295 >>> u = ui(); s = 'foo'
295 >>> u = ui(); s = 'foo'
296 >>> u.setconfig(s, 'true', 'yes')
296 >>> u.setconfig(s, 'true', 'yes')
297 >>> u.configbool(s, 'true')
297 >>> u.configbool(s, 'true')
298 True
298 True
299 >>> u.setconfig(s, 'false', 'no')
299 >>> u.setconfig(s, 'false', 'no')
300 >>> u.configbool(s, 'false')
300 >>> u.configbool(s, 'false')
301 False
301 False
302 >>> u.configbool(s, 'unknown')
302 >>> u.configbool(s, 'unknown')
303 False
303 False
304 >>> u.configbool(s, 'unknown', True)
304 >>> u.configbool(s, 'unknown', True)
305 True
305 True
306 >>> u.setconfig(s, 'invalid', 'somevalue')
306 >>> u.setconfig(s, 'invalid', 'somevalue')
307 >>> u.configbool(s, 'invalid')
307 >>> u.configbool(s, 'invalid')
308 Traceback (most recent call last):
308 Traceback (most recent call last):
309 ...
309 ...
310 ConfigError: foo.invalid is not a boolean ('somevalue')
310 ConfigError: foo.invalid is not a boolean ('somevalue')
311 """
311 """
312
312
313 v = self.config(section, name, None, untrusted)
313 v = self.config(section, name, None, untrusted)
314 if v is None:
314 if v is None:
315 return default
315 return default
316 if isinstance(v, bool):
316 if isinstance(v, bool):
317 return v
317 return v
318 b = util.parsebool(v)
318 b = util.parsebool(v)
319 if b is None:
319 if b is None:
320 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
320 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
321 % (section, name, v))
321 % (section, name, v))
322 return b
322 return b
323
323
324 def configint(self, section, name, default=None, untrusted=False):
324 def configint(self, section, name, default=None, untrusted=False):
325 """parse a configuration element as an integer
325 """parse a configuration element as an integer
326
326
327 >>> u = ui(); s = 'foo'
327 >>> u = ui(); s = 'foo'
328 >>> u.setconfig(s, 'int1', '42')
328 >>> u.setconfig(s, 'int1', '42')
329 >>> u.configint(s, 'int1')
329 >>> u.configint(s, 'int1')
330 42
330 42
331 >>> u.setconfig(s, 'int2', '-42')
331 >>> u.setconfig(s, 'int2', '-42')
332 >>> u.configint(s, 'int2')
332 >>> u.configint(s, 'int2')
333 -42
333 -42
334 >>> u.configint(s, 'unknown', 7)
334 >>> u.configint(s, 'unknown', 7)
335 7
335 7
336 >>> u.setconfig(s, 'invalid', 'somevalue')
336 >>> u.setconfig(s, 'invalid', 'somevalue')
337 >>> u.configint(s, 'invalid')
337 >>> u.configint(s, 'invalid')
338 Traceback (most recent call last):
338 Traceback (most recent call last):
339 ...
339 ...
340 ConfigError: foo.invalid is not an integer ('somevalue')
340 ConfigError: foo.invalid is not an integer ('somevalue')
341 """
341 """
342
342
343 v = self.config(section, name, None, untrusted)
343 v = self.config(section, name, None, untrusted)
344 if v is None:
344 if v is None:
345 return default
345 return default
346 try:
346 try:
347 return int(v)
347 return int(v)
348 except ValueError:
348 except ValueError:
349 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
349 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
350 % (section, name, v))
350 % (section, name, v))
351
351
352 def configbytes(self, section, name, default=0, untrusted=False):
352 def configbytes(self, section, name, default=0, untrusted=False):
353 """parse a configuration element as a quantity in bytes
353 """parse a configuration element as a quantity in bytes
354
354
355 Units can be specified as b (bytes), k or kb (kilobytes), m or
355 Units can be specified as b (bytes), k or kb (kilobytes), m or
356 mb (megabytes), g or gb (gigabytes).
356 mb (megabytes), g or gb (gigabytes).
357
357
358 >>> u = ui(); s = 'foo'
358 >>> u = ui(); s = 'foo'
359 >>> u.setconfig(s, 'val1', '42')
359 >>> u.setconfig(s, 'val1', '42')
360 >>> u.configbytes(s, 'val1')
360 >>> u.configbytes(s, 'val1')
361 42
361 42
362 >>> u.setconfig(s, 'val2', '42.5 kb')
362 >>> u.setconfig(s, 'val2', '42.5 kb')
363 >>> u.configbytes(s, 'val2')
363 >>> u.configbytes(s, 'val2')
364 43520
364 43520
365 >>> u.configbytes(s, 'unknown', '7 MB')
365 >>> u.configbytes(s, 'unknown', '7 MB')
366 7340032
366 7340032
367 >>> u.setconfig(s, 'invalid', 'somevalue')
367 >>> u.setconfig(s, 'invalid', 'somevalue')
368 >>> u.configbytes(s, 'invalid')
368 >>> u.configbytes(s, 'invalid')
369 Traceback (most recent call last):
369 Traceback (most recent call last):
370 ...
370 ...
371 ConfigError: foo.invalid is not a byte quantity ('somevalue')
371 ConfigError: foo.invalid is not a byte quantity ('somevalue')
372 """
372 """
373
373
374 value = self.config(section, name)
374 value = self.config(section, name)
375 if value is None:
375 if value is None:
376 if not isinstance(default, str):
376 if not isinstance(default, str):
377 return default
377 return default
378 value = default
378 value = default
379 try:
379 try:
380 return util.sizetoint(value)
380 return util.sizetoint(value)
381 except error.ParseError:
381 except error.ParseError:
382 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
382 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
383 % (section, name, value))
383 % (section, name, value))
384
384
385 def configlist(self, section, name, default=None, untrusted=False):
385 def configlist(self, section, name, default=None, untrusted=False):
386 """parse a configuration element as a list of comma/space separated
386 """parse a configuration element as a list of comma/space separated
387 strings
387 strings
388
388
389 >>> u = ui(); s = 'foo'
389 >>> u = ui(); s = 'foo'
390 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
390 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
391 >>> u.configlist(s, 'list1')
391 >>> u.configlist(s, 'list1')
392 ['this', 'is', 'a small', 'test']
392 ['this', 'is', 'a small', 'test']
393 """
393 """
394
394
395 def _parse_plain(parts, s, offset):
395 def _parse_plain(parts, s, offset):
396 whitespace = False
396 whitespace = False
397 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
397 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
398 whitespace = True
398 whitespace = True
399 offset += 1
399 offset += 1
400 if offset >= len(s):
400 if offset >= len(s):
401 return None, parts, offset
401 return None, parts, offset
402 if whitespace:
402 if whitespace:
403 parts.append('')
403 parts.append('')
404 if s[offset] == '"' and not parts[-1]:
404 if s[offset] == '"' and not parts[-1]:
405 return _parse_quote, parts, offset + 1
405 return _parse_quote, parts, offset + 1
406 elif s[offset] == '"' and parts[-1][-1] == '\\':
406 elif s[offset] == '"' and parts[-1][-1] == '\\':
407 parts[-1] = parts[-1][:-1] + s[offset]
407 parts[-1] = parts[-1][:-1] + s[offset]
408 return _parse_plain, parts, offset + 1
408 return _parse_plain, parts, offset + 1
409 parts[-1] += s[offset]
409 parts[-1] += s[offset]
410 return _parse_plain, parts, offset + 1
410 return _parse_plain, parts, offset + 1
411
411
412 def _parse_quote(parts, s, offset):
412 def _parse_quote(parts, s, offset):
413 if offset < len(s) and s[offset] == '"': # ""
413 if offset < len(s) and s[offset] == '"': # ""
414 parts.append('')
414 parts.append('')
415 offset += 1
415 offset += 1
416 while offset < len(s) and (s[offset].isspace() or
416 while offset < len(s) and (s[offset].isspace() or
417 s[offset] == ','):
417 s[offset] == ','):
418 offset += 1
418 offset += 1
419 return _parse_plain, parts, offset
419 return _parse_plain, parts, offset
420
420
421 while offset < len(s) and s[offset] != '"':
421 while offset < len(s) and s[offset] != '"':
422 if (s[offset] == '\\' and offset + 1 < len(s)
422 if (s[offset] == '\\' and offset + 1 < len(s)
423 and s[offset + 1] == '"'):
423 and s[offset + 1] == '"'):
424 offset += 1
424 offset += 1
425 parts[-1] += '"'
425 parts[-1] += '"'
426 else:
426 else:
427 parts[-1] += s[offset]
427 parts[-1] += s[offset]
428 offset += 1
428 offset += 1
429
429
430 if offset >= len(s):
430 if offset >= len(s):
431 real_parts = _configlist(parts[-1])
431 real_parts = _configlist(parts[-1])
432 if not real_parts:
432 if not real_parts:
433 parts[-1] = '"'
433 parts[-1] = '"'
434 else:
434 else:
435 real_parts[0] = '"' + real_parts[0]
435 real_parts[0] = '"' + real_parts[0]
436 parts = parts[:-1]
436 parts = parts[:-1]
437 parts.extend(real_parts)
437 parts.extend(real_parts)
438 return None, parts, offset
438 return None, parts, offset
439
439
440 offset += 1
440 offset += 1
441 while offset < len(s) and s[offset] in [' ', ',']:
441 while offset < len(s) and s[offset] in [' ', ',']:
442 offset += 1
442 offset += 1
443
443
444 if offset < len(s):
444 if offset < len(s):
445 if offset + 1 == len(s) and s[offset] == '"':
445 if offset + 1 == len(s) and s[offset] == '"':
446 parts[-1] += '"'
446 parts[-1] += '"'
447 offset += 1
447 offset += 1
448 else:
448 else:
449 parts.append('')
449 parts.append('')
450 else:
450 else:
451 return None, parts, offset
451 return None, parts, offset
452
452
453 return _parse_plain, parts, offset
453 return _parse_plain, parts, offset
454
454
455 def _configlist(s):
455 def _configlist(s):
456 s = s.rstrip(' ,')
456 s = s.rstrip(' ,')
457 if not s:
457 if not s:
458 return []
458 return []
459 parser, parts, offset = _parse_plain, [''], 0
459 parser, parts, offset = _parse_plain, [''], 0
460 while parser:
460 while parser:
461 parser, parts, offset = parser(parts, s, offset)
461 parser, parts, offset = parser(parts, s, offset)
462 return parts
462 return parts
463
463
464 result = self.config(section, name, untrusted=untrusted)
464 result = self.config(section, name, untrusted=untrusted)
465 if result is None:
465 if result is None:
466 result = default or []
466 result = default or []
467 if isinstance(result, basestring):
467 if isinstance(result, basestring):
468 result = _configlist(result.lstrip(' ,\n'))
468 result = _configlist(result.lstrip(' ,\n'))
469 if result is None:
469 if result is None:
470 result = default or []
470 result = default or []
471 return result
471 return result
472
472
473 def has_section(self, section, untrusted=False):
473 def has_section(self, section, untrusted=False):
474 '''tell whether section exists in config.'''
474 '''tell whether section exists in config.'''
475 return section in self._data(untrusted)
475 return section in self._data(untrusted)
476
476
477 def configitems(self, section, untrusted=False):
477 def configitems(self, section, untrusted=False):
478 items = self._data(untrusted).items(section)
478 items = self._data(untrusted).items(section)
479 if self.debugflag and not untrusted and self._reportuntrusted:
479 if self.debugflag and not untrusted and self._reportuntrusted:
480 for k, v in self._ucfg.items(section):
480 for k, v in self._ucfg.items(section):
481 if self._tcfg.get(section, k) != v:
481 if self._tcfg.get(section, k) != v:
482 self.debug("ignoring untrusted configuration option "
482 self.debug("ignoring untrusted configuration option "
483 "%s.%s = %s\n" % (section, k, v))
483 "%s.%s = %s\n" % (section, k, v))
484 return items
484 return items
485
485
486 def walkconfig(self, untrusted=False):
486 def walkconfig(self, untrusted=False):
487 cfg = self._data(untrusted)
487 cfg = self._data(untrusted)
488 for section in cfg.sections():
488 for section in cfg.sections():
489 for name, value in self.configitems(section, untrusted):
489 for name, value in self.configitems(section, untrusted):
490 yield section, name, value
490 yield section, name, value
491
491
492 def plain(self, feature=None):
492 def plain(self, feature=None):
493 '''is plain mode active?
493 '''is plain mode active?
494
494
495 Plain mode means that all configuration variables which affect
495 Plain mode means that all configuration variables which affect
496 the behavior and output of Mercurial should be
496 the behavior and output of Mercurial should be
497 ignored. Additionally, the output should be stable,
497 ignored. Additionally, the output should be stable,
498 reproducible and suitable for use in scripts or applications.
498 reproducible and suitable for use in scripts or applications.
499
499
500 The only way to trigger plain mode is by setting either the
500 The only way to trigger plain mode is by setting either the
501 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
501 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
502
502
503 The return value can either be
503 The return value can either be
504 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
504 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
505 - True otherwise
505 - True otherwise
506 '''
506 '''
507 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
507 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
508 return False
508 return False
509 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
509 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
510 if feature and exceptions:
510 if feature and exceptions:
511 return feature not in exceptions
511 return feature not in exceptions
512 return True
512 return True
513
513
514 def username(self):
514 def username(self):
515 """Return default username to be used in commits.
515 """Return default username to be used in commits.
516
516
517 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
517 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
518 and stop searching if one of these is set.
518 and stop searching if one of these is set.
519 If not found and ui.askusername is True, ask the user, else use
519 If not found and ui.askusername is True, ask the user, else use
520 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
520 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
521 """
521 """
522 user = os.environ.get("HGUSER")
522 user = os.environ.get("HGUSER")
523 if user is None:
523 if user is None:
524 user = self.config("ui", ["username", "user"])
524 user = self.config("ui", ["username", "user"])
525 if user is not None:
525 if user is not None:
526 user = os.path.expandvars(user)
526 user = os.path.expandvars(user)
527 if user is None:
527 if user is None:
528 user = os.environ.get("EMAIL")
528 user = os.environ.get("EMAIL")
529 if user is None and self.configbool("ui", "askusername"):
529 if user is None and self.configbool("ui", "askusername"):
530 user = self.prompt(_("enter a commit username:"), default=None)
530 user = self.prompt(_("enter a commit username:"), default=None)
531 if user is None and not self.interactive():
531 if user is None and not self.interactive():
532 try:
532 try:
533 user = '%s@%s' % (util.getuser(), socket.getfqdn())
533 user = '%s@%s' % (util.getuser(), socket.getfqdn())
534 self.warn(_("no username found, using '%s' instead\n") % user)
534 self.warn(_("no username found, using '%s' instead\n") % user)
535 except KeyError:
535 except KeyError:
536 pass
536 pass
537 if not user:
537 if not user:
538 raise util.Abort(_('no username supplied'),
538 raise util.Abort(_('no username supplied'),
539 hint=_('use "hg config --edit" '
539 hint=_('use "hg config --edit" '
540 'to set your username'))
540 'to set your username'))
541 if "\n" in user:
541 if "\n" in user:
542 raise util.Abort(_("username %s contains a newline\n") % repr(user))
542 raise util.Abort(_("username %s contains a newline\n") % repr(user))
543 return user
543 return user
544
544
545 def shortuser(self, user):
545 def shortuser(self, user):
546 """Return a short representation of a user name or email address."""
546 """Return a short representation of a user name or email address."""
547 if not self.verbose:
547 if not self.verbose:
548 user = util.shortuser(user)
548 user = util.shortuser(user)
549 return user
549 return user
550
550
551 def expandpath(self, loc, default=None):
551 def expandpath(self, loc, default=None):
552 """Return repository location relative to cwd or from [paths]"""
552 """Return repository location relative to cwd or from [paths]"""
553 try:
553 try:
554 p = self.paths.getpath(loc)
554 p = self.paths.getpath(loc)
555 if p:
555 if p:
556 return p.rawloc
556 return p.rawloc
557 except error.RepoError:
557 except error.RepoError:
558 pass
558 pass
559
559
560 if default:
560 if default:
561 try:
561 try:
562 p = self.paths.getpath(default)
562 p = self.paths.getpath(default)
563 if p:
563 if p:
564 return p.rawloc
564 return p.rawloc
565 except error.RepoError:
565 except error.RepoError:
566 pass
566 pass
567
567
568 return loc
568 return loc
569
569
570 @util.propertycache
570 @util.propertycache
571 def paths(self):
571 def paths(self):
572 return paths(self)
572 return paths(self)
573
573
574 def pushbuffer(self, error=False, subproc=False):
574 def pushbuffer(self, error=False, subproc=False):
575 """install a buffer to capture standard output of the ui object
575 """install a buffer to capture standard output of the ui object
576
576
577 If error is True, the error output will be captured too.
577 If error is True, the error output will be captured too.
578
578
579 If subproc is True, output from subprocesses (typically hooks) will be
579 If subproc is True, output from subprocesses (typically hooks) will be
580 captured too."""
580 captured too."""
581 self._buffers.append([])
581 self._buffers.append([])
582 self._bufferstates.append((error, subproc))
582 self._bufferstates.append((error, subproc))
583
583
584 def popbuffer(self, labeled=False):
584 def popbuffer(self, labeled=False):
585 '''pop the last buffer and return the buffered output
585 '''pop the last buffer and return the buffered output
586
586
587 If labeled is True, any labels associated with buffered
587 If labeled is True, any labels associated with buffered
588 output will be handled. By default, this has no effect
588 output will be handled. By default, this has no effect
589 on the output returned, but extensions and GUI tools may
589 on the output returned, but extensions and GUI tools may
590 handle this argument and returned styled output. If output
590 handle this argument and returned styled output. If output
591 is being buffered so it can be captured and parsed or
591 is being buffered so it can be captured and parsed or
592 processed, labeled should not be set to True.
592 processed, labeled should not be set to True.
593 '''
593 '''
594 self._bufferstates.pop()
594 self._bufferstates.pop()
595 return "".join(self._buffers.pop())
595 return "".join(self._buffers.pop())
596
596
597 def write(self, *args, **opts):
597 def write(self, *args, **opts):
598 '''write args to output
598 '''write args to output
599
599
600 By default, this method simply writes to the buffer or stdout,
600 By default, this method simply writes to the buffer or stdout,
601 but extensions or GUI tools may override this method,
601 but extensions or GUI tools may override this method,
602 write_err(), popbuffer(), and label() to style output from
602 write_err(), popbuffer(), and label() to style output from
603 various parts of hg.
603 various parts of hg.
604
604
605 An optional keyword argument, "label", can be passed in.
605 An optional keyword argument, "label", can be passed in.
606 This should be a string containing label names separated by
606 This should be a string containing label names separated by
607 space. Label names take the form of "topic.type". For example,
607 space. Label names take the form of "topic.type". For example,
608 ui.debug() issues a label of "ui.debug".
608 ui.debug() issues a label of "ui.debug".
609
609
610 When labeling output for a specific command, a label of
610 When labeling output for a specific command, a label of
611 "cmdname.type" is recommended. For example, status issues
611 "cmdname.type" is recommended. For example, status issues
612 a label of "status.modified" for modified files.
612 a label of "status.modified" for modified files.
613 '''
613 '''
614 self._progclear()
614 self._progclear()
615 if self._buffers:
615 if self._buffers:
616 self._buffers[-1].extend([str(a) for a in args])
616 self._buffers[-1].extend([str(a) for a in args])
617 else:
617 else:
618 for a in args:
618 for a in args:
619 self.fout.write(str(a))
619 self.fout.write(str(a))
620
620
621 def write_err(self, *args, **opts):
621 def write_err(self, *args, **opts):
622 self._progclear()
622 self._progclear()
623 try:
623 try:
624 if self._bufferstates and self._bufferstates[-1][0]:
624 if self._bufferstates and self._bufferstates[-1][0]:
625 return self.write(*args, **opts)
625 return self.write(*args, **opts)
626 if not getattr(self.fout, 'closed', False):
626 if not getattr(self.fout, 'closed', False):
627 self.fout.flush()
627 self.fout.flush()
628 for a in args:
628 for a in args:
629 self.ferr.write(str(a))
629 self.ferr.write(str(a))
630 # stderr may be buffered under win32 when redirected to files,
630 # stderr may be buffered under win32 when redirected to files,
631 # including stdout.
631 # including stdout.
632 if not getattr(self.ferr, 'closed', False):
632 if not getattr(self.ferr, 'closed', False):
633 self.ferr.flush()
633 self.ferr.flush()
634 except IOError as inst:
634 except IOError as inst:
635 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
635 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
636 raise
636 raise
637
637
638 def flush(self):
638 def flush(self):
639 try: self.fout.flush()
639 try: self.fout.flush()
640 except (IOError, ValueError): pass
640 except (IOError, ValueError): pass
641 try: self.ferr.flush()
641 try: self.ferr.flush()
642 except (IOError, ValueError): pass
642 except (IOError, ValueError): pass
643
643
644 def _isatty(self, fh):
644 def _isatty(self, fh):
645 if self.configbool('ui', 'nontty', False):
645 if self.configbool('ui', 'nontty', False):
646 return False
646 return False
647 return util.isatty(fh)
647 return util.isatty(fh)
648
648
649 def interactive(self):
649 def interactive(self):
650 '''is interactive input allowed?
650 '''is interactive input allowed?
651
651
652 An interactive session is a session where input can be reasonably read
652 An interactive session is a session where input can be reasonably read
653 from `sys.stdin'. If this function returns false, any attempt to read
653 from `sys.stdin'. If this function returns false, any attempt to read
654 from stdin should fail with an error, unless a sensible default has been
654 from stdin should fail with an error, unless a sensible default has been
655 specified.
655 specified.
656
656
657 Interactiveness is triggered by the value of the `ui.interactive'
657 Interactiveness is triggered by the value of the `ui.interactive'
658 configuration variable or - if it is unset - when `sys.stdin' points
658 configuration variable or - if it is unset - when `sys.stdin' points
659 to a terminal device.
659 to a terminal device.
660
660
661 This function refers to input only; for output, see `ui.formatted()'.
661 This function refers to input only; for output, see `ui.formatted()'.
662 '''
662 '''
663 i = self.configbool("ui", "interactive", None)
663 i = self.configbool("ui", "interactive", None)
664 if i is None:
664 if i is None:
665 # some environments replace stdin without implementing isatty
665 # some environments replace stdin without implementing isatty
666 # usually those are non-interactive
666 # usually those are non-interactive
667 return self._isatty(self.fin)
667 return self._isatty(self.fin)
668
668
669 return i
669 return i
670
670
671 def termwidth(self):
671 def termwidth(self):
672 '''how wide is the terminal in columns?
672 '''how wide is the terminal in columns?
673 '''
673 '''
674 if 'COLUMNS' in os.environ:
674 if 'COLUMNS' in os.environ:
675 try:
675 try:
676 return int(os.environ['COLUMNS'])
676 return int(os.environ['COLUMNS'])
677 except ValueError:
677 except ValueError:
678 pass
678 pass
679 return util.termwidth()
679 return util.termwidth()
680
680
681 def formatted(self):
681 def formatted(self):
682 '''should formatted output be used?
682 '''should formatted output be used?
683
683
684 It is often desirable to format the output to suite the output medium.
684 It is often desirable to format the output to suite the output medium.
685 Examples of this are truncating long lines or colorizing messages.
685 Examples of this are truncating long lines or colorizing messages.
686 However, this is not often not desirable when piping output into other
686 However, this is not often not desirable when piping output into other
687 utilities, e.g. `grep'.
687 utilities, e.g. `grep'.
688
688
689 Formatted output is triggered by the value of the `ui.formatted'
689 Formatted output is triggered by the value of the `ui.formatted'
690 configuration variable or - if it is unset - when `sys.stdout' points
690 configuration variable or - if it is unset - when `sys.stdout' points
691 to a terminal device. Please note that `ui.formatted' should be
691 to a terminal device. Please note that `ui.formatted' should be
692 considered an implementation detail; it is not intended for use outside
692 considered an implementation detail; it is not intended for use outside
693 Mercurial or its extensions.
693 Mercurial or its extensions.
694
694
695 This function refers to output only; for input, see `ui.interactive()'.
695 This function refers to output only; for input, see `ui.interactive()'.
696 This function always returns false when in plain mode, see `ui.plain()'.
696 This function always returns false when in plain mode, see `ui.plain()'.
697 '''
697 '''
698 if self.plain():
698 if self.plain():
699 return False
699 return False
700
700
701 i = self.configbool("ui", "formatted", None)
701 i = self.configbool("ui", "formatted", None)
702 if i is None:
702 if i is None:
703 # some environments replace stdout without implementing isatty
703 # some environments replace stdout without implementing isatty
704 # usually those are non-interactive
704 # usually those are non-interactive
705 return self._isatty(self.fout)
705 return self._isatty(self.fout)
706
706
707 return i
707 return i
708
708
709 def _readline(self, prompt=''):
709 def _readline(self, prompt=''):
710 if self._isatty(self.fin):
710 if self._isatty(self.fin):
711 try:
711 try:
712 # magically add command line editing support, where
712 # magically add command line editing support, where
713 # available
713 # available
714 import readline
714 import readline
715 # force demandimport to really load the module
715 # force demandimport to really load the module
716 readline.read_history_file
716 readline.read_history_file
717 # windows sometimes raises something other than ImportError
717 # windows sometimes raises something other than ImportError
718 except Exception:
718 except Exception:
719 pass
719 pass
720
720
721 # call write() so output goes through subclassed implementation
721 # call write() so output goes through subclassed implementation
722 # e.g. color extension on Windows
722 # e.g. color extension on Windows
723 self.write(prompt)
723 self.write(prompt)
724
724
725 # instead of trying to emulate raw_input, swap (self.fin,
725 # instead of trying to emulate raw_input, swap (self.fin,
726 # self.fout) with (sys.stdin, sys.stdout)
726 # self.fout) with (sys.stdin, sys.stdout)
727 oldin = sys.stdin
727 oldin = sys.stdin
728 oldout = sys.stdout
728 oldout = sys.stdout
729 sys.stdin = self.fin
729 sys.stdin = self.fin
730 sys.stdout = self.fout
730 sys.stdout = self.fout
731 # prompt ' ' must exist; otherwise readline may delete entire line
731 # prompt ' ' must exist; otherwise readline may delete entire line
732 # - http://bugs.python.org/issue12833
732 # - http://bugs.python.org/issue12833
733 line = raw_input(' ')
733 line = raw_input(' ')
734 sys.stdin = oldin
734 sys.stdin = oldin
735 sys.stdout = oldout
735 sys.stdout = oldout
736
736
737 # When stdin is in binary mode on Windows, it can cause
737 # When stdin is in binary mode on Windows, it can cause
738 # raw_input() to emit an extra trailing carriage return
738 # raw_input() to emit an extra trailing carriage return
739 if os.linesep == '\r\n' and line and line[-1] == '\r':
739 if os.linesep == '\r\n' and line and line[-1] == '\r':
740 line = line[:-1]
740 line = line[:-1]
741 return line
741 return line
742
742
743 def prompt(self, msg, default="y"):
743 def prompt(self, msg, default="y"):
744 """Prompt user with msg, read response.
744 """Prompt user with msg, read response.
745 If ui is not interactive, the default is returned.
745 If ui is not interactive, the default is returned.
746 """
746 """
747 if not self.interactive():
747 if not self.interactive():
748 self.write(msg, ' ', default, "\n")
748 self.write(msg, ' ', default, "\n")
749 return default
749 return default
750 try:
750 try:
751 r = self._readline(self.label(msg, 'ui.prompt'))
751 r = self._readline(self.label(msg, 'ui.prompt'))
752 if not r:
752 if not r:
753 r = default
753 r = default
754 if self.configbool('ui', 'promptecho'):
754 if self.configbool('ui', 'promptecho'):
755 self.write(r, "\n")
755 self.write(r, "\n")
756 return r
756 return r
757 except EOFError:
757 except EOFError:
758 raise util.Abort(_('response expected'))
758 raise util.Abort(_('response expected'))
759
759
760 @staticmethod
760 @staticmethod
761 def extractchoices(prompt):
761 def extractchoices(prompt):
762 """Extract prompt message and list of choices from specified prompt.
762 """Extract prompt message and list of choices from specified prompt.
763
763
764 This returns tuple "(message, choices)", and "choices" is the
764 This returns tuple "(message, choices)", and "choices" is the
765 list of tuple "(response character, text without &)".
765 list of tuple "(response character, text without &)".
766 """
766 """
767 parts = prompt.split('$$')
767 parts = prompt.split('$$')
768 msg = parts[0].rstrip(' ')
768 msg = parts[0].rstrip(' ')
769 choices = [p.strip(' ') for p in parts[1:]]
769 choices = [p.strip(' ') for p in parts[1:]]
770 return (msg,
770 return (msg,
771 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
771 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
772 for s in choices])
772 for s in choices])
773
773
774 def promptchoice(self, prompt, default=0):
774 def promptchoice(self, prompt, default=0):
775 """Prompt user with a message, read response, and ensure it matches
775 """Prompt user with a message, read response, and ensure it matches
776 one of the provided choices. The prompt is formatted as follows:
776 one of the provided choices. The prompt is formatted as follows:
777
777
778 "would you like fries with that (Yn)? $$ &Yes $$ &No"
778 "would you like fries with that (Yn)? $$ &Yes $$ &No"
779
779
780 The index of the choice is returned. Responses are case
780 The index of the choice is returned. Responses are case
781 insensitive. If ui is not interactive, the default is
781 insensitive. If ui is not interactive, the default is
782 returned.
782 returned.
783 """
783 """
784
784
785 msg, choices = self.extractchoices(prompt)
785 msg, choices = self.extractchoices(prompt)
786 resps = [r for r, t in choices]
786 resps = [r for r, t in choices]
787 while True:
787 while True:
788 r = self.prompt(msg, resps[default])
788 r = self.prompt(msg, resps[default])
789 if r.lower() in resps:
789 if r.lower() in resps:
790 return resps.index(r.lower())
790 return resps.index(r.lower())
791 self.write(_("unrecognized response\n"))
791 self.write(_("unrecognized response\n"))
792
792
793 def getpass(self, prompt=None, default=None):
793 def getpass(self, prompt=None, default=None):
794 if not self.interactive():
794 if not self.interactive():
795 return default
795 return default
796 try:
796 try:
797 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
797 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
798 # disable getpass() only if explicitly specified. it's still valid
798 # disable getpass() only if explicitly specified. it's still valid
799 # to interact with tty even if fin is not a tty.
799 # to interact with tty even if fin is not a tty.
800 if self.configbool('ui', 'nontty'):
800 if self.configbool('ui', 'nontty'):
801 return self.fin.readline().rstrip('\n')
801 return self.fin.readline().rstrip('\n')
802 else:
802 else:
803 return getpass.getpass('')
803 return getpass.getpass('')
804 except EOFError:
804 except EOFError:
805 raise util.Abort(_('response expected'))
805 raise util.Abort(_('response expected'))
806 def status(self, *msg, **opts):
806 def status(self, *msg, **opts):
807 '''write status message to output (if ui.quiet is False)
807 '''write status message to output (if ui.quiet is False)
808
808
809 This adds an output label of "ui.status".
809 This adds an output label of "ui.status".
810 '''
810 '''
811 if not self.quiet:
811 if not self.quiet:
812 opts['label'] = opts.get('label', '') + ' ui.status'
812 opts['label'] = opts.get('label', '') + ' ui.status'
813 self.write(*msg, **opts)
813 self.write(*msg, **opts)
814 def warn(self, *msg, **opts):
814 def warn(self, *msg, **opts):
815 '''write warning message to output (stderr)
815 '''write warning message to output (stderr)
816
816
817 This adds an output label of "ui.warning".
817 This adds an output label of "ui.warning".
818 '''
818 '''
819 opts['label'] = opts.get('label', '') + ' ui.warning'
819 opts['label'] = opts.get('label', '') + ' ui.warning'
820 self.write_err(*msg, **opts)
820 self.write_err(*msg, **opts)
821 def note(self, *msg, **opts):
821 def note(self, *msg, **opts):
822 '''write note to output (if ui.verbose is True)
822 '''write note to output (if ui.verbose is True)
823
823
824 This adds an output label of "ui.note".
824 This adds an output label of "ui.note".
825 '''
825 '''
826 if self.verbose:
826 if self.verbose:
827 opts['label'] = opts.get('label', '') + ' ui.note'
827 opts['label'] = opts.get('label', '') + ' ui.note'
828 self.write(*msg, **opts)
828 self.write(*msg, **opts)
829 def debug(self, *msg, **opts):
829 def debug(self, *msg, **opts):
830 '''write debug message to output (if ui.debugflag is True)
830 '''write debug message to output (if ui.debugflag is True)
831
831
832 This adds an output label of "ui.debug".
832 This adds an output label of "ui.debug".
833 '''
833 '''
834 if self.debugflag:
834 if self.debugflag:
835 opts['label'] = opts.get('label', '') + ' ui.debug'
835 opts['label'] = opts.get('label', '') + ' ui.debug'
836 self.write(*msg, **opts)
836 self.write(*msg, **opts)
837 def edit(self, text, user, extra=None, editform=None):
837 def edit(self, text, user, extra=None, editform=None):
838 if extra is None:
838 if extra is None:
839 extra = {}
839 extra = {}
840 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
840 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
841 text=True)
841 text=True)
842 try:
842 try:
843 f = os.fdopen(fd, "w")
843 f = os.fdopen(fd, "w")
844 f.write(text)
844 f.write(text)
845 f.close()
845 f.close()
846
846
847 environ = {'HGUSER': user}
847 environ = {'HGUSER': user}
848 if 'transplant_source' in extra:
848 if 'transplant_source' in extra:
849 environ.update({'HGREVISION': hex(extra['transplant_source'])})
849 environ.update({'HGREVISION': hex(extra['transplant_source'])})
850 for label in ('intermediate-source', 'source', 'rebase_source'):
850 for label in ('intermediate-source', 'source', 'rebase_source'):
851 if label in extra:
851 if label in extra:
852 environ.update({'HGREVISION': extra[label]})
852 environ.update({'HGREVISION': extra[label]})
853 break
853 break
854 if editform:
854 if editform:
855 environ.update({'HGEDITFORM': editform})
855 environ.update({'HGEDITFORM': editform})
856
856
857 editor = self.geteditor()
857 editor = self.geteditor()
858
858
859 self.system("%s \"%s\"" % (editor, name),
859 self.system("%s \"%s\"" % (editor, name),
860 environ=environ,
860 environ=environ,
861 onerr=util.Abort, errprefix=_("edit failed"))
861 onerr=util.Abort, errprefix=_("edit failed"))
862
862
863 f = open(name)
863 f = open(name)
864 t = f.read()
864 t = f.read()
865 f.close()
865 f.close()
866 finally:
866 finally:
867 os.unlink(name)
867 os.unlink(name)
868
868
869 return t
869 return t
870
870
871 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
871 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
872 '''execute shell command with appropriate output stream. command
872 '''execute shell command with appropriate output stream. command
873 output will be redirected if fout is not stdout.
873 output will be redirected if fout is not stdout.
874 '''
874 '''
875 out = self.fout
875 out = self.fout
876 if any(s[1] for s in self._bufferstates):
876 if any(s[1] for s in self._bufferstates):
877 out = self
877 out = self
878 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
878 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
879 errprefix=errprefix, out=out)
879 errprefix=errprefix, out=out)
880
880
881 def traceback(self, exc=None, force=False):
881 def traceback(self, exc=None, force=False):
882 '''print exception traceback if traceback printing enabled or forced.
882 '''print exception traceback if traceback printing enabled or forced.
883 only to call in exception handler. returns true if traceback
883 only to call in exception handler. returns true if traceback
884 printed.'''
884 printed.'''
885 if self.tracebackflag or force:
885 if self.tracebackflag or force:
886 if exc is None:
886 if exc is None:
887 exc = sys.exc_info()
887 exc = sys.exc_info()
888 cause = getattr(exc[1], 'cause', None)
888 cause = getattr(exc[1], 'cause', None)
889
889
890 if cause is not None:
890 if cause is not None:
891 causetb = traceback.format_tb(cause[2])
891 causetb = traceback.format_tb(cause[2])
892 exctb = traceback.format_tb(exc[2])
892 exctb = traceback.format_tb(exc[2])
893 exconly = traceback.format_exception_only(cause[0], cause[1])
893 exconly = traceback.format_exception_only(cause[0], cause[1])
894
894
895 # exclude frame where 'exc' was chained and rethrown from exctb
895 # exclude frame where 'exc' was chained and rethrown from exctb
896 self.write_err('Traceback (most recent call last):\n',
896 self.write_err('Traceback (most recent call last):\n',
897 ''.join(exctb[:-1]),
897 ''.join(exctb[:-1]),
898 ''.join(causetb),
898 ''.join(causetb),
899 ''.join(exconly))
899 ''.join(exconly))
900 else:
900 else:
901 output = traceback.format_exception(exc[0], exc[1], exc[2])
901 output = traceback.format_exception(exc[0], exc[1], exc[2])
902 self.write_err(''.join(output))
902 self.write_err(''.join(output))
903 return self.tracebackflag or force
903 return self.tracebackflag or force
904
904
905 def geteditor(self):
905 def geteditor(self):
906 '''return editor to use'''
906 '''return editor to use'''
907 if sys.platform == 'plan9':
907 if sys.platform == 'plan9':
908 # vi is the MIPS instruction simulator on Plan 9. We
908 # vi is the MIPS instruction simulator on Plan 9. We
909 # instead default to E to plumb commit messages to
909 # instead default to E to plumb commit messages to
910 # avoid confusion.
910 # avoid confusion.
911 editor = 'E'
911 editor = 'E'
912 else:
912 else:
913 editor = 'vi'
913 editor = 'vi'
914 return (os.environ.get("HGEDITOR") or
914 return (os.environ.get("HGEDITOR") or
915 self.config("ui", "editor") or
915 self.config("ui", "editor") or
916 os.environ.get("VISUAL") or
916 os.environ.get("VISUAL") or
917 os.environ.get("EDITOR", editor))
917 os.environ.get("EDITOR", editor))
918
918
919 @util.propertycache
919 @util.propertycache
920 def _progbar(self):
920 def _progbar(self):
921 """setup the progbar singleton to the ui object"""
921 """setup the progbar singleton to the ui object"""
922 if (self.quiet or self.debugflag
922 if (self.quiet or self.debugflag
923 or self.configbool('progress', 'disable', False)
923 or self.configbool('progress', 'disable', False)
924 or not progress.shouldprint(self)):
924 or not progress.shouldprint(self)):
925 return None
925 return None
926 return getprogbar(self)
926 return getprogbar(self)
927
927
928 def _progclear(self):
928 def _progclear(self):
929 """clear progress bar output if any. use it before any output"""
929 """clear progress bar output if any. use it before any output"""
930 if '_progbar' not in vars(self): # nothing loadef yet
930 if '_progbar' not in vars(self): # nothing loadef yet
931 return
931 return
932 if self._progbar is not None and self._progbar.printed:
932 if self._progbar is not None and self._progbar.printed:
933 self._progbar.clear()
933 self._progbar.clear()
934
934
935 def progress(self, topic, pos, item="", unit="", total=None):
935 def progress(self, topic, pos, item="", unit="", total=None):
936 '''show a progress message
936 '''show a progress message
937
937
938 With stock hg, this is simply a debug message that is hidden
938 With stock hg, this is simply a debug message that is hidden
939 by default, but with extensions or GUI tools it may be
939 by default, but with extensions or GUI tools it may be
940 visible. 'topic' is the current operation, 'item' is a
940 visible. 'topic' is the current operation, 'item' is a
941 non-numeric marker of the current position (i.e. the currently
941 non-numeric marker of the current position (i.e. the currently
942 in-process file), 'pos' is the current numeric position (i.e.
942 in-process file), 'pos' is the current numeric position (i.e.
943 revision, bytes, etc.), unit is a corresponding unit label,
943 revision, bytes, etc.), unit is a corresponding unit label,
944 and total is the highest expected pos.
944 and total is the highest expected pos.
945
945
946 Multiple nested topics may be active at a time.
946 Multiple nested topics may be active at a time.
947
947
948 All topics should be marked closed by setting pos to None at
948 All topics should be marked closed by setting pos to None at
949 termination.
949 termination.
950 '''
950 '''
951 if self._progbar is not None:
951 if self._progbar is not None:
952 self._progbar.progress(topic, pos, item=item, unit=unit,
952 self._progbar.progress(topic, pos, item=item, unit=unit,
953 total=total)
953 total=total)
954 if pos is None or not self.configbool('progress', 'debug'):
954 if pos is None or not self.configbool('progress', 'debug'):
955 return
955 return
956
956
957 if unit:
957 if unit:
958 unit = ' ' + unit
958 unit = ' ' + unit
959 if item:
959 if item:
960 item = ' ' + item
960 item = ' ' + item
961
961
962 if total:
962 if total:
963 pct = 100.0 * pos / total
963 pct = 100.0 * pos / total
964 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
964 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
965 % (topic, item, pos, total, unit, pct))
965 % (topic, item, pos, total, unit, pct))
966 else:
966 else:
967 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
967 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
968
968
969 def log(self, service, *msg, **opts):
969 def log(self, service, *msg, **opts):
970 '''hook for logging facility extensions
970 '''hook for logging facility extensions
971
971
972 service should be a readily-identifiable subsystem, which will
972 service should be a readily-identifiable subsystem, which will
973 allow filtering.
973 allow filtering.
974
974
975 *msg should be a newline-terminated format string to log, and
975 *msg should be a newline-terminated format string to log, and
976 then any values to %-format into that format string.
976 then any values to %-format into that format string.
977
977
978 **opts currently has no defined meanings.
978 **opts currently has no defined meanings.
979 '''
979 '''
980
980
981 def label(self, msg, label):
981 def label(self, msg, label):
982 '''style msg based on supplied label
982 '''style msg based on supplied label
983
983
984 Like ui.write(), this just returns msg unchanged, but extensions
984 Like ui.write(), this just returns msg unchanged, but extensions
985 and GUI tools can override it to allow styling output without
985 and GUI tools can override it to allow styling output without
986 writing it.
986 writing it.
987
987
988 ui.write(s, 'label') is equivalent to
988 ui.write(s, 'label') is equivalent to
989 ui.write(ui.label(s, 'label')).
989 ui.write(ui.label(s, 'label')).
990 '''
990 '''
991 return msg
991 return msg
992
992
993 def develwarn(self, msg):
993 def develwarn(self, msg):
994 """issue a developer warning message"""
994 """issue a developer warning message"""
995 msg = 'devel-warn: ' + msg
995 msg = 'devel-warn: ' + msg
996 if self.tracebackflag:
996 if self.tracebackflag:
997 util.debugstacktrace(msg, 2)
997 util.debugstacktrace(msg, 2, self.ferr, self.fout)
998 else:
998 else:
999 curframe = inspect.currentframe()
999 curframe = inspect.currentframe()
1000 calframe = inspect.getouterframes(curframe, 2)
1000 calframe = inspect.getouterframes(curframe, 2)
1001 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1001 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1002
1002
1003 class paths(dict):
1003 class paths(dict):
1004 """Represents a collection of paths and their configs.
1004 """Represents a collection of paths and their configs.
1005
1005
1006 Data is initially derived from ui instances and the config files they have
1006 Data is initially derived from ui instances and the config files they have
1007 loaded.
1007 loaded.
1008 """
1008 """
1009 def __init__(self, ui):
1009 def __init__(self, ui):
1010 dict.__init__(self)
1010 dict.__init__(self)
1011
1011
1012 for name, loc in ui.configitems('paths'):
1012 for name, loc in ui.configitems('paths'):
1013 # No location is the same as not existing.
1013 # No location is the same as not existing.
1014 if not loc:
1014 if not loc:
1015 continue
1015 continue
1016
1016
1017 # TODO ignore default-push once all consumers stop referencing it
1017 # TODO ignore default-push once all consumers stop referencing it
1018 # since it is handled specifically below.
1018 # since it is handled specifically below.
1019
1019
1020 self[name] = path(name, rawloc=loc)
1020 self[name] = path(name, rawloc=loc)
1021
1021
1022 # Handle default-push, which is a one-off that defines the push URL for
1022 # Handle default-push, which is a one-off that defines the push URL for
1023 # the "default" path.
1023 # the "default" path.
1024 defaultpush = ui.config('paths', 'default-push')
1024 defaultpush = ui.config('paths', 'default-push')
1025 if defaultpush and 'default' in self:
1025 if defaultpush and 'default' in self:
1026 self['default']._pushloc = defaultpush
1026 self['default']._pushloc = defaultpush
1027
1027
1028 def getpath(self, name, default=None):
1028 def getpath(self, name, default=None):
1029 """Return a ``path`` from a string, falling back to a default.
1029 """Return a ``path`` from a string, falling back to a default.
1030
1030
1031 ``name`` can be a named path or locations. Locations are filesystem
1031 ``name`` can be a named path or locations. Locations are filesystem
1032 paths or URIs.
1032 paths or URIs.
1033
1033
1034 Returns None if ``name`` is not a registered path, a URI, or a local
1034 Returns None if ``name`` is not a registered path, a URI, or a local
1035 path to a repo.
1035 path to a repo.
1036 """
1036 """
1037 # Only fall back to default if no path was requested.
1037 # Only fall back to default if no path was requested.
1038 if name is None:
1038 if name is None:
1039 if default:
1039 if default:
1040 try:
1040 try:
1041 return self[default]
1041 return self[default]
1042 except KeyError:
1042 except KeyError:
1043 return None
1043 return None
1044 else:
1044 else:
1045 return None
1045 return None
1046
1046
1047 # Most likely empty string.
1047 # Most likely empty string.
1048 # This may need to raise in the future.
1048 # This may need to raise in the future.
1049 if not name:
1049 if not name:
1050 return None
1050 return None
1051
1051
1052 try:
1052 try:
1053 return self[name]
1053 return self[name]
1054 except KeyError:
1054 except KeyError:
1055 # Try to resolve as a local path or URI.
1055 # Try to resolve as a local path or URI.
1056 try:
1056 try:
1057 return path(None, rawloc=name)
1057 return path(None, rawloc=name)
1058 except ValueError:
1058 except ValueError:
1059 raise error.RepoError(_('repository %s does not exist') %
1059 raise error.RepoError(_('repository %s does not exist') %
1060 name)
1060 name)
1061
1061
1062 assert False
1062 assert False
1063
1063
1064 class path(object):
1064 class path(object):
1065 """Represents an individual path and its configuration."""
1065 """Represents an individual path and its configuration."""
1066
1066
1067 def __init__(self, name, rawloc=None, pushloc=None):
1067 def __init__(self, name, rawloc=None, pushloc=None):
1068 """Construct a path from its config options.
1068 """Construct a path from its config options.
1069
1069
1070 ``name`` is the symbolic name of the path.
1070 ``name`` is the symbolic name of the path.
1071 ``rawloc`` is the raw location, as defined in the config.
1071 ``rawloc`` is the raw location, as defined in the config.
1072 ``pushloc`` is the raw locations pushes should be made to.
1072 ``pushloc`` is the raw locations pushes should be made to.
1073
1073
1074 If ``name`` is not defined, we require that the location be a) a local
1074 If ``name`` is not defined, we require that the location be a) a local
1075 filesystem path with a .hg directory or b) a URL. If not,
1075 filesystem path with a .hg directory or b) a URL. If not,
1076 ``ValueError`` is raised.
1076 ``ValueError`` is raised.
1077 """
1077 """
1078 if not rawloc:
1078 if not rawloc:
1079 raise ValueError('rawloc must be defined')
1079 raise ValueError('rawloc must be defined')
1080
1080
1081 # Locations may define branches via syntax <base>#<branch>.
1081 # Locations may define branches via syntax <base>#<branch>.
1082 u = util.url(rawloc)
1082 u = util.url(rawloc)
1083 branch = None
1083 branch = None
1084 if u.fragment:
1084 if u.fragment:
1085 branch = u.fragment
1085 branch = u.fragment
1086 u.fragment = None
1086 u.fragment = None
1087
1087
1088 self.url = u
1088 self.url = u
1089 self.branch = branch
1089 self.branch = branch
1090
1090
1091 self.name = name
1091 self.name = name
1092 self.rawloc = rawloc
1092 self.rawloc = rawloc
1093 self.loc = str(u)
1093 self.loc = str(u)
1094 self._pushloc = pushloc
1094 self._pushloc = pushloc
1095
1095
1096 # When given a raw location but not a symbolic name, validate the
1096 # When given a raw location but not a symbolic name, validate the
1097 # location is valid.
1097 # location is valid.
1098 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1098 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1099 raise ValueError('location is not a URL or path to a local '
1099 raise ValueError('location is not a URL or path to a local '
1100 'repo: %s' % rawloc)
1100 'repo: %s' % rawloc)
1101
1101
1102 def _isvalidlocalpath(self, path):
1102 def _isvalidlocalpath(self, path):
1103 """Returns True if the given path is a potentially valid repository.
1103 """Returns True if the given path is a potentially valid repository.
1104 This is its own function so that extensions can change the definition of
1104 This is its own function so that extensions can change the definition of
1105 'valid' in this case (like when pulling from a git repo into a hg
1105 'valid' in this case (like when pulling from a git repo into a hg
1106 one)."""
1106 one)."""
1107 return os.path.isdir(os.path.join(path, '.hg'))
1107 return os.path.isdir(os.path.join(path, '.hg'))
1108
1108
1109 @property
1109 @property
1110 def pushloc(self):
1110 def pushloc(self):
1111 return self._pushloc or self.loc
1111 return self._pushloc or self.loc
1112
1112
1113 # we instantiate one globally shared progress bar to avoid
1113 # we instantiate one globally shared progress bar to avoid
1114 # competing progress bars when multiple UI objects get created
1114 # competing progress bars when multiple UI objects get created
1115 _progresssingleton = None
1115 _progresssingleton = None
1116
1116
1117 def getprogbar(ui):
1117 def getprogbar(ui):
1118 global _progresssingleton
1118 global _progresssingleton
1119 if _progresssingleton is None:
1119 if _progresssingleton is None:
1120 # passing 'ui' object to the singleton is fishy,
1120 # passing 'ui' object to the singleton is fishy,
1121 # this is how the extension used to work but feel free to rework it.
1121 # this is how the extension used to work but feel free to rework it.
1122 _progresssingleton = progress.progbar(ui)
1122 _progresssingleton = progress.progbar(ui)
1123 return _progresssingleton
1123 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now