##// END OF EJS Templates
ui: flush stderr after printing a non-chained exception for Windows...
Matt Harbison -
r25568:c1ff82da default
parent child Browse files
Show More
@@ -1,1017 +1,1016 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, progress
10 import config, scmutil, util, error, formatter, progress
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 self._progclear()
587 self._progclear()
588 if self._buffers:
588 if self._buffers:
589 self._buffers[-1].extend([str(a) for a in args])
589 self._buffers[-1].extend([str(a) for a in args])
590 else:
590 else:
591 for a in args:
591 for a in args:
592 self.fout.write(str(a))
592 self.fout.write(str(a))
593
593
594 def write_err(self, *args, **opts):
594 def write_err(self, *args, **opts):
595 self._progclear()
595 self._progclear()
596 try:
596 try:
597 if self._bufferstates and self._bufferstates[-1][0]:
597 if self._bufferstates and self._bufferstates[-1][0]:
598 return self.write(*args, **opts)
598 return self.write(*args, **opts)
599 if not getattr(self.fout, 'closed', False):
599 if not getattr(self.fout, 'closed', False):
600 self.fout.flush()
600 self.fout.flush()
601 for a in args:
601 for a in args:
602 self.ferr.write(str(a))
602 self.ferr.write(str(a))
603 # stderr may be buffered under win32 when redirected to files,
603 # stderr may be buffered under win32 when redirected to files,
604 # including stdout.
604 # including stdout.
605 if not getattr(self.ferr, 'closed', False):
605 if not getattr(self.ferr, 'closed', False):
606 self.ferr.flush()
606 self.ferr.flush()
607 except IOError, inst:
607 except IOError, inst:
608 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
608 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
609 raise
609 raise
610
610
611 def flush(self):
611 def flush(self):
612 try: self.fout.flush()
612 try: self.fout.flush()
613 except (IOError, ValueError): pass
613 except (IOError, ValueError): pass
614 try: self.ferr.flush()
614 try: self.ferr.flush()
615 except (IOError, ValueError): pass
615 except (IOError, ValueError): pass
616
616
617 def _isatty(self, fh):
617 def _isatty(self, fh):
618 if self.configbool('ui', 'nontty', False):
618 if self.configbool('ui', 'nontty', False):
619 return False
619 return False
620 return util.isatty(fh)
620 return util.isatty(fh)
621
621
622 def interactive(self):
622 def interactive(self):
623 '''is interactive input allowed?
623 '''is interactive input allowed?
624
624
625 An interactive session is a session where input can be reasonably read
625 An interactive session is a session where input can be reasonably read
626 from `sys.stdin'. If this function returns false, any attempt to read
626 from `sys.stdin'. If this function returns false, any attempt to read
627 from stdin should fail with an error, unless a sensible default has been
627 from stdin should fail with an error, unless a sensible default has been
628 specified.
628 specified.
629
629
630 Interactiveness is triggered by the value of the `ui.interactive'
630 Interactiveness is triggered by the value of the `ui.interactive'
631 configuration variable or - if it is unset - when `sys.stdin' points
631 configuration variable or - if it is unset - when `sys.stdin' points
632 to a terminal device.
632 to a terminal device.
633
633
634 This function refers to input only; for output, see `ui.formatted()'.
634 This function refers to input only; for output, see `ui.formatted()'.
635 '''
635 '''
636 i = self.configbool("ui", "interactive", None)
636 i = self.configbool("ui", "interactive", None)
637 if i is None:
637 if i is None:
638 # some environments replace stdin without implementing isatty
638 # some environments replace stdin without implementing isatty
639 # usually those are non-interactive
639 # usually those are non-interactive
640 return self._isatty(self.fin)
640 return self._isatty(self.fin)
641
641
642 return i
642 return i
643
643
644 def termwidth(self):
644 def termwidth(self):
645 '''how wide is the terminal in columns?
645 '''how wide is the terminal in columns?
646 '''
646 '''
647 if 'COLUMNS' in os.environ:
647 if 'COLUMNS' in os.environ:
648 try:
648 try:
649 return int(os.environ['COLUMNS'])
649 return int(os.environ['COLUMNS'])
650 except ValueError:
650 except ValueError:
651 pass
651 pass
652 return util.termwidth()
652 return util.termwidth()
653
653
654 def formatted(self):
654 def formatted(self):
655 '''should formatted output be used?
655 '''should formatted output be used?
656
656
657 It is often desirable to format the output to suite the output medium.
657 It is often desirable to format the output to suite the output medium.
658 Examples of this are truncating long lines or colorizing messages.
658 Examples of this are truncating long lines or colorizing messages.
659 However, this is not often not desirable when piping output into other
659 However, this is not often not desirable when piping output into other
660 utilities, e.g. `grep'.
660 utilities, e.g. `grep'.
661
661
662 Formatted output is triggered by the value of the `ui.formatted'
662 Formatted output is triggered by the value of the `ui.formatted'
663 configuration variable or - if it is unset - when `sys.stdout' points
663 configuration variable or - if it is unset - when `sys.stdout' points
664 to a terminal device. Please note that `ui.formatted' should be
664 to a terminal device. Please note that `ui.formatted' should be
665 considered an implementation detail; it is not intended for use outside
665 considered an implementation detail; it is not intended for use outside
666 Mercurial or its extensions.
666 Mercurial or its extensions.
667
667
668 This function refers to output only; for input, see `ui.interactive()'.
668 This function refers to output only; for input, see `ui.interactive()'.
669 This function always returns false when in plain mode, see `ui.plain()'.
669 This function always returns false when in plain mode, see `ui.plain()'.
670 '''
670 '''
671 if self.plain():
671 if self.plain():
672 return False
672 return False
673
673
674 i = self.configbool("ui", "formatted", None)
674 i = self.configbool("ui", "formatted", None)
675 if i is None:
675 if i is None:
676 # some environments replace stdout without implementing isatty
676 # some environments replace stdout without implementing isatty
677 # usually those are non-interactive
677 # usually those are non-interactive
678 return self._isatty(self.fout)
678 return self._isatty(self.fout)
679
679
680 return i
680 return i
681
681
682 def _readline(self, prompt=''):
682 def _readline(self, prompt=''):
683 if self._isatty(self.fin):
683 if self._isatty(self.fin):
684 try:
684 try:
685 # magically add command line editing support, where
685 # magically add command line editing support, where
686 # available
686 # available
687 import readline
687 import readline
688 # force demandimport to really load the module
688 # force demandimport to really load the module
689 readline.read_history_file
689 readline.read_history_file
690 # windows sometimes raises something other than ImportError
690 # windows sometimes raises something other than ImportError
691 except Exception:
691 except Exception:
692 pass
692 pass
693
693
694 # call write() so output goes through subclassed implementation
694 # call write() so output goes through subclassed implementation
695 # e.g. color extension on Windows
695 # e.g. color extension on Windows
696 self.write(prompt)
696 self.write(prompt)
697
697
698 # instead of trying to emulate raw_input, swap (self.fin,
698 # instead of trying to emulate raw_input, swap (self.fin,
699 # self.fout) with (sys.stdin, sys.stdout)
699 # self.fout) with (sys.stdin, sys.stdout)
700 oldin = sys.stdin
700 oldin = sys.stdin
701 oldout = sys.stdout
701 oldout = sys.stdout
702 sys.stdin = self.fin
702 sys.stdin = self.fin
703 sys.stdout = self.fout
703 sys.stdout = self.fout
704 # prompt ' ' must exist; otherwise readline may delete entire line
704 # prompt ' ' must exist; otherwise readline may delete entire line
705 # - http://bugs.python.org/issue12833
705 # - http://bugs.python.org/issue12833
706 line = raw_input(' ')
706 line = raw_input(' ')
707 sys.stdin = oldin
707 sys.stdin = oldin
708 sys.stdout = oldout
708 sys.stdout = oldout
709
709
710 # When stdin is in binary mode on Windows, it can cause
710 # When stdin is in binary mode on Windows, it can cause
711 # raw_input() to emit an extra trailing carriage return
711 # raw_input() to emit an extra trailing carriage return
712 if os.linesep == '\r\n' and line and line[-1] == '\r':
712 if os.linesep == '\r\n' and line and line[-1] == '\r':
713 line = line[:-1]
713 line = line[:-1]
714 return line
714 return line
715
715
716 def prompt(self, msg, default="y"):
716 def prompt(self, msg, default="y"):
717 """Prompt user with msg, read response.
717 """Prompt user with msg, read response.
718 If ui is not interactive, the default is returned.
718 If ui is not interactive, the default is returned.
719 """
719 """
720 if not self.interactive():
720 if not self.interactive():
721 self.write(msg, ' ', default, "\n")
721 self.write(msg, ' ', default, "\n")
722 return default
722 return default
723 try:
723 try:
724 r = self._readline(self.label(msg, 'ui.prompt'))
724 r = self._readline(self.label(msg, 'ui.prompt'))
725 if not r:
725 if not r:
726 r = default
726 r = default
727 if self.configbool('ui', 'promptecho'):
727 if self.configbool('ui', 'promptecho'):
728 self.write(r, "\n")
728 self.write(r, "\n")
729 return r
729 return r
730 except EOFError:
730 except EOFError:
731 raise util.Abort(_('response expected'))
731 raise util.Abort(_('response expected'))
732
732
733 @staticmethod
733 @staticmethod
734 def extractchoices(prompt):
734 def extractchoices(prompt):
735 """Extract prompt message and list of choices from specified prompt.
735 """Extract prompt message and list of choices from specified prompt.
736
736
737 This returns tuple "(message, choices)", and "choices" is the
737 This returns tuple "(message, choices)", and "choices" is the
738 list of tuple "(response character, text without &)".
738 list of tuple "(response character, text without &)".
739 """
739 """
740 parts = prompt.split('$$')
740 parts = prompt.split('$$')
741 msg = parts[0].rstrip(' ')
741 msg = parts[0].rstrip(' ')
742 choices = [p.strip(' ') for p in parts[1:]]
742 choices = [p.strip(' ') for p in parts[1:]]
743 return (msg,
743 return (msg,
744 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
744 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
745 for s in choices])
745 for s in choices])
746
746
747 def promptchoice(self, prompt, default=0):
747 def promptchoice(self, prompt, default=0):
748 """Prompt user with a message, read response, and ensure it matches
748 """Prompt user with a message, read response, and ensure it matches
749 one of the provided choices. The prompt is formatted as follows:
749 one of the provided choices. The prompt is formatted as follows:
750
750
751 "would you like fries with that (Yn)? $$ &Yes $$ &No"
751 "would you like fries with that (Yn)? $$ &Yes $$ &No"
752
752
753 The index of the choice is returned. Responses are case
753 The index of the choice is returned. Responses are case
754 insensitive. If ui is not interactive, the default is
754 insensitive. If ui is not interactive, the default is
755 returned.
755 returned.
756 """
756 """
757
757
758 msg, choices = self.extractchoices(prompt)
758 msg, choices = self.extractchoices(prompt)
759 resps = [r for r, t in choices]
759 resps = [r for r, t in choices]
760 while True:
760 while True:
761 r = self.prompt(msg, resps[default])
761 r = self.prompt(msg, resps[default])
762 if r.lower() in resps:
762 if r.lower() in resps:
763 return resps.index(r.lower())
763 return resps.index(r.lower())
764 self.write(_("unrecognized response\n"))
764 self.write(_("unrecognized response\n"))
765
765
766 def getpass(self, prompt=None, default=None):
766 def getpass(self, prompt=None, default=None):
767 if not self.interactive():
767 if not self.interactive():
768 return default
768 return default
769 try:
769 try:
770 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
770 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
771 # disable getpass() only if explicitly specified. it's still valid
771 # disable getpass() only if explicitly specified. it's still valid
772 # to interact with tty even if fin is not a tty.
772 # to interact with tty even if fin is not a tty.
773 if self.configbool('ui', 'nontty'):
773 if self.configbool('ui', 'nontty'):
774 return self.fin.readline().rstrip('\n')
774 return self.fin.readline().rstrip('\n')
775 else:
775 else:
776 return getpass.getpass('')
776 return getpass.getpass('')
777 except EOFError:
777 except EOFError:
778 raise util.Abort(_('response expected'))
778 raise util.Abort(_('response expected'))
779 def status(self, *msg, **opts):
779 def status(self, *msg, **opts):
780 '''write status message to output (if ui.quiet is False)
780 '''write status message to output (if ui.quiet is False)
781
781
782 This adds an output label of "ui.status".
782 This adds an output label of "ui.status".
783 '''
783 '''
784 if not self.quiet:
784 if not self.quiet:
785 opts['label'] = opts.get('label', '') + ' ui.status'
785 opts['label'] = opts.get('label', '') + ' ui.status'
786 self.write(*msg, **opts)
786 self.write(*msg, **opts)
787 def warn(self, *msg, **opts):
787 def warn(self, *msg, **opts):
788 '''write warning message to output (stderr)
788 '''write warning message to output (stderr)
789
789
790 This adds an output label of "ui.warning".
790 This adds an output label of "ui.warning".
791 '''
791 '''
792 opts['label'] = opts.get('label', '') + ' ui.warning'
792 opts['label'] = opts.get('label', '') + ' ui.warning'
793 self.write_err(*msg, **opts)
793 self.write_err(*msg, **opts)
794 def note(self, *msg, **opts):
794 def note(self, *msg, **opts):
795 '''write note to output (if ui.verbose is True)
795 '''write note to output (if ui.verbose is True)
796
796
797 This adds an output label of "ui.note".
797 This adds an output label of "ui.note".
798 '''
798 '''
799 if self.verbose:
799 if self.verbose:
800 opts['label'] = opts.get('label', '') + ' ui.note'
800 opts['label'] = opts.get('label', '') + ' ui.note'
801 self.write(*msg, **opts)
801 self.write(*msg, **opts)
802 def debug(self, *msg, **opts):
802 def debug(self, *msg, **opts):
803 '''write debug message to output (if ui.debugflag is True)
803 '''write debug message to output (if ui.debugflag is True)
804
804
805 This adds an output label of "ui.debug".
805 This adds an output label of "ui.debug".
806 '''
806 '''
807 if self.debugflag:
807 if self.debugflag:
808 opts['label'] = opts.get('label', '') + ' ui.debug'
808 opts['label'] = opts.get('label', '') + ' ui.debug'
809 self.write(*msg, **opts)
809 self.write(*msg, **opts)
810 def edit(self, text, user, extra={}, editform=None):
810 def edit(self, text, user, extra={}, editform=None):
811 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
811 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
812 text=True)
812 text=True)
813 try:
813 try:
814 f = os.fdopen(fd, "w")
814 f = os.fdopen(fd, "w")
815 f.write(text)
815 f.write(text)
816 f.close()
816 f.close()
817
817
818 environ = {'HGUSER': user}
818 environ = {'HGUSER': user}
819 if 'transplant_source' in extra:
819 if 'transplant_source' in extra:
820 environ.update({'HGREVISION': hex(extra['transplant_source'])})
820 environ.update({'HGREVISION': hex(extra['transplant_source'])})
821 for label in ('intermediate-source', 'source', 'rebase_source'):
821 for label in ('intermediate-source', 'source', 'rebase_source'):
822 if label in extra:
822 if label in extra:
823 environ.update({'HGREVISION': extra[label]})
823 environ.update({'HGREVISION': extra[label]})
824 break
824 break
825 if editform:
825 if editform:
826 environ.update({'HGEDITFORM': editform})
826 environ.update({'HGEDITFORM': editform})
827
827
828 editor = self.geteditor()
828 editor = self.geteditor()
829
829
830 self.system("%s \"%s\"" % (editor, name),
830 self.system("%s \"%s\"" % (editor, name),
831 environ=environ,
831 environ=environ,
832 onerr=util.Abort, errprefix=_("edit failed"))
832 onerr=util.Abort, errprefix=_("edit failed"))
833
833
834 f = open(name)
834 f = open(name)
835 t = f.read()
835 t = f.read()
836 f.close()
836 f.close()
837 finally:
837 finally:
838 os.unlink(name)
838 os.unlink(name)
839
839
840 return t
840 return t
841
841
842 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
842 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
843 '''execute shell command with appropriate output stream. command
843 '''execute shell command with appropriate output stream. command
844 output will be redirected if fout is not stdout.
844 output will be redirected if fout is not stdout.
845 '''
845 '''
846 out = self.fout
846 out = self.fout
847 if any(s[1] for s in self._bufferstates):
847 if any(s[1] for s in self._bufferstates):
848 out = self
848 out = self
849 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
849 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
850 errprefix=errprefix, out=out)
850 errprefix=errprefix, out=out)
851
851
852 def traceback(self, exc=None, force=False):
852 def traceback(self, exc=None, force=False):
853 '''print exception traceback if traceback printing enabled or forced.
853 '''print exception traceback if traceback printing enabled or forced.
854 only to call in exception handler. returns true if traceback
854 only to call in exception handler. returns true if traceback
855 printed.'''
855 printed.'''
856 if self.tracebackflag or force:
856 if self.tracebackflag or force:
857 if exc is None:
857 if exc is None:
858 exc = sys.exc_info()
858 exc = sys.exc_info()
859 cause = getattr(exc[1], 'cause', None)
859 cause = getattr(exc[1], 'cause', None)
860
860
861 if cause is not None:
861 if cause is not None:
862 causetb = traceback.format_tb(cause[2])
862 causetb = traceback.format_tb(cause[2])
863 exctb = traceback.format_tb(exc[2])
863 exctb = traceback.format_tb(exc[2])
864 exconly = traceback.format_exception_only(cause[0], cause[1])
864 exconly = traceback.format_exception_only(cause[0], cause[1])
865
865
866 # exclude frame where 'exc' was chained and rethrown from exctb
866 # exclude frame where 'exc' was chained and rethrown from exctb
867 self.write_err('Traceback (most recent call last):\n',
867 self.write_err('Traceback (most recent call last):\n',
868 ''.join(exctb[:-1]),
868 ''.join(exctb[:-1]),
869 ''.join(causetb),
869 ''.join(causetb),
870 ''.join(exconly))
870 ''.join(exconly))
871 else:
871 else:
872 self.flush() # flush debug or status message
872 output = traceback.format_exception(exc[0], exc[1], exc[2])
873 traceback.print_exception(exc[0], exc[1], exc[2],
873 self.write_err(''.join(output))
874 file=self.ferr)
875 return self.tracebackflag or force
874 return self.tracebackflag or force
876
875
877 def geteditor(self):
876 def geteditor(self):
878 '''return editor to use'''
877 '''return editor to use'''
879 if sys.platform == 'plan9':
878 if sys.platform == 'plan9':
880 # vi is the MIPS instruction simulator on Plan 9. We
879 # vi is the MIPS instruction simulator on Plan 9. We
881 # instead default to E to plumb commit messages to
880 # instead default to E to plumb commit messages to
882 # avoid confusion.
881 # avoid confusion.
883 editor = 'E'
882 editor = 'E'
884 else:
883 else:
885 editor = 'vi'
884 editor = 'vi'
886 return (os.environ.get("HGEDITOR") or
885 return (os.environ.get("HGEDITOR") or
887 self.config("ui", "editor") or
886 self.config("ui", "editor") or
888 os.environ.get("VISUAL") or
887 os.environ.get("VISUAL") or
889 os.environ.get("EDITOR", editor))
888 os.environ.get("EDITOR", editor))
890
889
891 @util.propertycache
890 @util.propertycache
892 def _progbar(self):
891 def _progbar(self):
893 """setup the progbar singleton to the ui object"""
892 """setup the progbar singleton to the ui object"""
894 if (self.quiet or self.debugflag
893 if (self.quiet or self.debugflag
895 or self.configbool('progress', 'disable', False)
894 or self.configbool('progress', 'disable', False)
896 or not progress.shouldprint(self)):
895 or not progress.shouldprint(self)):
897 return None
896 return None
898 return getprogbar(self)
897 return getprogbar(self)
899
898
900 def _progclear(self):
899 def _progclear(self):
901 """clear progress bar output if any. use it before any output"""
900 """clear progress bar output if any. use it before any output"""
902 if '_progbar' not in vars(self): # nothing loadef yet
901 if '_progbar' not in vars(self): # nothing loadef yet
903 return
902 return
904 if self._progbar is not None and self._progbar.printed:
903 if self._progbar is not None and self._progbar.printed:
905 self._progbar.clear()
904 self._progbar.clear()
906
905
907 def progress(self, topic, pos, item="", unit="", total=None):
906 def progress(self, topic, pos, item="", unit="", total=None):
908 '''show a progress message
907 '''show a progress message
909
908
910 With stock hg, this is simply a debug message that is hidden
909 With stock hg, this is simply a debug message that is hidden
911 by default, but with extensions or GUI tools it may be
910 by default, but with extensions or GUI tools it may be
912 visible. 'topic' is the current operation, 'item' is a
911 visible. 'topic' is the current operation, 'item' is a
913 non-numeric marker of the current position (i.e. the currently
912 non-numeric marker of the current position (i.e. the currently
914 in-process file), 'pos' is the current numeric position (i.e.
913 in-process file), 'pos' is the current numeric position (i.e.
915 revision, bytes, etc.), unit is a corresponding unit label,
914 revision, bytes, etc.), unit is a corresponding unit label,
916 and total is the highest expected pos.
915 and total is the highest expected pos.
917
916
918 Multiple nested topics may be active at a time.
917 Multiple nested topics may be active at a time.
919
918
920 All topics should be marked closed by setting pos to None at
919 All topics should be marked closed by setting pos to None at
921 termination.
920 termination.
922 '''
921 '''
923 if self._progbar is not None:
922 if self._progbar is not None:
924 self._progbar.progress(topic, pos, item=item, unit=unit,
923 self._progbar.progress(topic, pos, item=item, unit=unit,
925 total=total)
924 total=total)
926 if pos is None or not self.configbool('progress', 'debug'):
925 if pos is None or not self.configbool('progress', 'debug'):
927 return
926 return
928
927
929 if unit:
928 if unit:
930 unit = ' ' + unit
929 unit = ' ' + unit
931 if item:
930 if item:
932 item = ' ' + item
931 item = ' ' + item
933
932
934 if total:
933 if total:
935 pct = 100.0 * pos / total
934 pct = 100.0 * pos / total
936 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
935 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
937 % (topic, item, pos, total, unit, pct))
936 % (topic, item, pos, total, unit, pct))
938 else:
937 else:
939 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
938 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
940
939
941 def log(self, service, *msg, **opts):
940 def log(self, service, *msg, **opts):
942 '''hook for logging facility extensions
941 '''hook for logging facility extensions
943
942
944 service should be a readily-identifiable subsystem, which will
943 service should be a readily-identifiable subsystem, which will
945 allow filtering.
944 allow filtering.
946 message should be a newline-terminated string to log.
945 message should be a newline-terminated string to log.
947 '''
946 '''
948 pass
947 pass
949
948
950 def label(self, msg, label):
949 def label(self, msg, label):
951 '''style msg based on supplied label
950 '''style msg based on supplied label
952
951
953 Like ui.write(), this just returns msg unchanged, but extensions
952 Like ui.write(), this just returns msg unchanged, but extensions
954 and GUI tools can override it to allow styling output without
953 and GUI tools can override it to allow styling output without
955 writing it.
954 writing it.
956
955
957 ui.write(s, 'label') is equivalent to
956 ui.write(s, 'label') is equivalent to
958 ui.write(ui.label(s, 'label')).
957 ui.write(ui.label(s, 'label')).
959 '''
958 '''
960 return msg
959 return msg
961
960
962 class paths(dict):
961 class paths(dict):
963 """Represents a collection of paths and their configs.
962 """Represents a collection of paths and their configs.
964
963
965 Data is initially derived from ui instances and the config files they have
964 Data is initially derived from ui instances and the config files they have
966 loaded.
965 loaded.
967 """
966 """
968 def __init__(self, ui):
967 def __init__(self, ui):
969 dict.__init__(self)
968 dict.__init__(self)
970
969
971 for name, loc in ui.configitems('paths'):
970 for name, loc in ui.configitems('paths'):
972 # No location is the same as not existing.
971 # No location is the same as not existing.
973 if not loc:
972 if not loc:
974 continue
973 continue
975 self[name] = path(name, rawloc=loc)
974 self[name] = path(name, rawloc=loc)
976
975
977 def getpath(self, name, default=None):
976 def getpath(self, name, default=None):
978 """Return a ``path`` for the specified name, falling back to a default.
977 """Return a ``path`` for the specified name, falling back to a default.
979
978
980 Returns the first of ``name`` or ``default`` that is present, or None
979 Returns the first of ``name`` or ``default`` that is present, or None
981 if neither is present.
980 if neither is present.
982 """
981 """
983 try:
982 try:
984 return self[name]
983 return self[name]
985 except KeyError:
984 except KeyError:
986 if default is not None:
985 if default is not None:
987 try:
986 try:
988 return self[default]
987 return self[default]
989 except KeyError:
988 except KeyError:
990 pass
989 pass
991
990
992 return None
991 return None
993
992
994 class path(object):
993 class path(object):
995 """Represents an individual path and its configuration."""
994 """Represents an individual path and its configuration."""
996
995
997 def __init__(self, name, rawloc=None):
996 def __init__(self, name, rawloc=None):
998 """Construct a path from its config options.
997 """Construct a path from its config options.
999
998
1000 ``name`` is the symbolic name of the path.
999 ``name`` is the symbolic name of the path.
1001 ``rawloc`` is the raw location, as defined in the config.
1000 ``rawloc`` is the raw location, as defined in the config.
1002 """
1001 """
1003 self.name = name
1002 self.name = name
1004 # We'll do more intelligent things with rawloc in the future.
1003 # We'll do more intelligent things with rawloc in the future.
1005 self.loc = rawloc
1004 self.loc = rawloc
1006
1005
1007 # we instantiate one globally shared progress bar to avoid
1006 # we instantiate one globally shared progress bar to avoid
1008 # competing progress bars when multiple UI objects get created
1007 # competing progress bars when multiple UI objects get created
1009 _progresssingleton = None
1008 _progresssingleton = None
1010
1009
1011 def getprogbar(ui):
1010 def getprogbar(ui):
1012 global _progresssingleton
1011 global _progresssingleton
1013 if _progresssingleton is None:
1012 if _progresssingleton is None:
1014 # passing 'ui' object to the singleton is fishy,
1013 # passing 'ui' object to the singleton is fishy,
1015 # this is how the extension used to work but feel free to rework it.
1014 # this is how the extension used to work but feel free to rework it.
1016 _progresssingleton = progress.progbar(ui)
1015 _progresssingleton = progress.progbar(ui)
1017 return _progresssingleton
1016 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now