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