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