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