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