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