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