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