##// END OF EJS Templates
ui: optionally ignore sub-options from configitems()...
Gregory Szorc -
r27253:f43988e5 default
parent child Browse files
Show More
@@ -1,1175 +1,1181 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):
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:
516 newitems = {}
517 for k, v in items:
518 if ':' not in k:
519 newitems[k] = v
520 items = newitems.items()
515 if self.debugflag and not untrusted and self._reportuntrusted:
521 if self.debugflag and not untrusted and self._reportuntrusted:
516 for k, v in self._ucfg.items(section):
522 for k, v in self._ucfg.items(section):
517 if self._tcfg.get(section, k) != v:
523 if self._tcfg.get(section, k) != v:
518 self.debug("ignoring untrusted configuration option "
524 self.debug("ignoring untrusted configuration option "
519 "%s.%s = %s\n" % (section, k, v))
525 "%s.%s = %s\n" % (section, k, v))
520 return items
526 return items
521
527
522 def walkconfig(self, untrusted=False):
528 def walkconfig(self, untrusted=False):
523 cfg = self._data(untrusted)
529 cfg = self._data(untrusted)
524 for section in cfg.sections():
530 for section in cfg.sections():
525 for name, value in self.configitems(section, untrusted):
531 for name, value in self.configitems(section, untrusted):
526 yield section, name, value
532 yield section, name, value
527
533
528 def plain(self, feature=None):
534 def plain(self, feature=None):
529 '''is plain mode active?
535 '''is plain mode active?
530
536
531 Plain mode means that all configuration variables which affect
537 Plain mode means that all configuration variables which affect
532 the behavior and output of Mercurial should be
538 the behavior and output of Mercurial should be
533 ignored. Additionally, the output should be stable,
539 ignored. Additionally, the output should be stable,
534 reproducible and suitable for use in scripts or applications.
540 reproducible and suitable for use in scripts or applications.
535
541
536 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
537 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
543 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
538
544
539 The return value can either be
545 The return value can either be
540 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
546 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
541 - True otherwise
547 - True otherwise
542 '''
548 '''
543 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:
544 return False
550 return False
545 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
551 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
546 if feature and exceptions:
552 if feature and exceptions:
547 return feature not in exceptions
553 return feature not in exceptions
548 return True
554 return True
549
555
550 def username(self):
556 def username(self):
551 """Return default username to be used in commits.
557 """Return default username to be used in commits.
552
558
553 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
559 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
554 and stop searching if one of these is set.
560 and stop searching if one of these is set.
555 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
556 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
562 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
557 """
563 """
558 user = os.environ.get("HGUSER")
564 user = os.environ.get("HGUSER")
559 if user is None:
565 if user is None:
560 user = self.config("ui", ["username", "user"])
566 user = self.config("ui", ["username", "user"])
561 if user is not None:
567 if user is not None:
562 user = os.path.expandvars(user)
568 user = os.path.expandvars(user)
563 if user is None:
569 if user is None:
564 user = os.environ.get("EMAIL")
570 user = os.environ.get("EMAIL")
565 if user is None and self.configbool("ui", "askusername"):
571 if user is None and self.configbool("ui", "askusername"):
566 user = self.prompt(_("enter a commit username:"), default=None)
572 user = self.prompt(_("enter a commit username:"), default=None)
567 if user is None and not self.interactive():
573 if user is None and not self.interactive():
568 try:
574 try:
569 user = '%s@%s' % (util.getuser(), socket.getfqdn())
575 user = '%s@%s' % (util.getuser(), socket.getfqdn())
570 self.warn(_("no username found, using '%s' instead\n") % user)
576 self.warn(_("no username found, using '%s' instead\n") % user)
571 except KeyError:
577 except KeyError:
572 pass
578 pass
573 if not user:
579 if not user:
574 raise error.Abort(_('no username supplied'),
580 raise error.Abort(_('no username supplied'),
575 hint=_('use "hg config --edit" '
581 hint=_('use "hg config --edit" '
576 'to set your username'))
582 'to set your username'))
577 if "\n" in user:
583 if "\n" in user:
578 raise error.Abort(_("username %s contains a newline\n")
584 raise error.Abort(_("username %s contains a newline\n")
579 % repr(user))
585 % repr(user))
580 return user
586 return user
581
587
582 def shortuser(self, user):
588 def shortuser(self, user):
583 """Return a short representation of a user name or email address."""
589 """Return a short representation of a user name or email address."""
584 if not self.verbose:
590 if not self.verbose:
585 user = util.shortuser(user)
591 user = util.shortuser(user)
586 return user
592 return user
587
593
588 def expandpath(self, loc, default=None):
594 def expandpath(self, loc, default=None):
589 """Return repository location relative to cwd or from [paths]"""
595 """Return repository location relative to cwd or from [paths]"""
590 try:
596 try:
591 p = self.paths.getpath(loc)
597 p = self.paths.getpath(loc)
592 if p:
598 if p:
593 return p.rawloc
599 return p.rawloc
594 except error.RepoError:
600 except error.RepoError:
595 pass
601 pass
596
602
597 if default:
603 if default:
598 try:
604 try:
599 p = self.paths.getpath(default)
605 p = self.paths.getpath(default)
600 if p:
606 if p:
601 return p.rawloc
607 return p.rawloc
602 except error.RepoError:
608 except error.RepoError:
603 pass
609 pass
604
610
605 return loc
611 return loc
606
612
607 @util.propertycache
613 @util.propertycache
608 def paths(self):
614 def paths(self):
609 return paths(self)
615 return paths(self)
610
616
611 def pushbuffer(self, error=False, subproc=False, labeled=False):
617 def pushbuffer(self, error=False, subproc=False, labeled=False):
612 """install a buffer to capture standard output of the ui object
618 """install a buffer to capture standard output of the ui object
613
619
614 If error is True, the error output will be captured too.
620 If error is True, the error output will be captured too.
615
621
616 If subproc is True, output from subprocesses (typically hooks) will be
622 If subproc is True, output from subprocesses (typically hooks) will be
617 captured too.
623 captured too.
618
624
619 If labeled is True, any labels associated with buffered
625 If labeled is True, any labels associated with buffered
620 output will be handled. By default, this has no effect
626 output will be handled. By default, this has no effect
621 on the output returned, but extensions and GUI tools may
627 on the output returned, but extensions and GUI tools may
622 handle this argument and returned styled output. If output
628 handle this argument and returned styled output. If output
623 is being buffered so it can be captured and parsed or
629 is being buffered so it can be captured and parsed or
624 processed, labeled should not be set to True.
630 processed, labeled should not be set to True.
625 """
631 """
626 self._buffers.append([])
632 self._buffers.append([])
627 self._bufferstates.append((error, subproc, labeled))
633 self._bufferstates.append((error, subproc, labeled))
628 self._bufferapplylabels = labeled
634 self._bufferapplylabels = labeled
629
635
630 def popbuffer(self):
636 def popbuffer(self):
631 '''pop the last buffer and return the buffered output'''
637 '''pop the last buffer and return the buffered output'''
632 self._bufferstates.pop()
638 self._bufferstates.pop()
633 if self._bufferstates:
639 if self._bufferstates:
634 self._bufferapplylabels = self._bufferstates[-1][2]
640 self._bufferapplylabels = self._bufferstates[-1][2]
635 else:
641 else:
636 self._bufferapplylabels = None
642 self._bufferapplylabels = None
637
643
638 return "".join(self._buffers.pop())
644 return "".join(self._buffers.pop())
639
645
640 def write(self, *args, **opts):
646 def write(self, *args, **opts):
641 '''write args to output
647 '''write args to output
642
648
643 By default, this method simply writes to the buffer or stdout,
649 By default, this method simply writes to the buffer or stdout,
644 but extensions or GUI tools may override this method,
650 but extensions or GUI tools may override this method,
645 write_err(), popbuffer(), and label() to style output from
651 write_err(), popbuffer(), and label() to style output from
646 various parts of hg.
652 various parts of hg.
647
653
648 An optional keyword argument, "label", can be passed in.
654 An optional keyword argument, "label", can be passed in.
649 This should be a string containing label names separated by
655 This should be a string containing label names separated by
650 space. Label names take the form of "topic.type". For example,
656 space. Label names take the form of "topic.type". For example,
651 ui.debug() issues a label of "ui.debug".
657 ui.debug() issues a label of "ui.debug".
652
658
653 When labeling output for a specific command, a label of
659 When labeling output for a specific command, a label of
654 "cmdname.type" is recommended. For example, status issues
660 "cmdname.type" is recommended. For example, status issues
655 a label of "status.modified" for modified files.
661 a label of "status.modified" for modified files.
656 '''
662 '''
657 if self._buffers:
663 if self._buffers:
658 self._buffers[-1].extend(a for a in args)
664 self._buffers[-1].extend(a for a in args)
659 else:
665 else:
660 self._progclear()
666 self._progclear()
661 for a in args:
667 for a in args:
662 self.fout.write(a)
668 self.fout.write(a)
663
669
664 def write_err(self, *args, **opts):
670 def write_err(self, *args, **opts):
665 self._progclear()
671 self._progclear()
666 try:
672 try:
667 if self._bufferstates and self._bufferstates[-1][0]:
673 if self._bufferstates and self._bufferstates[-1][0]:
668 return self.write(*args, **opts)
674 return self.write(*args, **opts)
669 if not getattr(self.fout, 'closed', False):
675 if not getattr(self.fout, 'closed', False):
670 self.fout.flush()
676 self.fout.flush()
671 for a in args:
677 for a in args:
672 self.ferr.write(a)
678 self.ferr.write(a)
673 # stderr may be buffered under win32 when redirected to files,
679 # stderr may be buffered under win32 when redirected to files,
674 # including stdout.
680 # including stdout.
675 if not getattr(self.ferr, 'closed', False):
681 if not getattr(self.ferr, 'closed', False):
676 self.ferr.flush()
682 self.ferr.flush()
677 except IOError as inst:
683 except IOError as inst:
678 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
684 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
679 raise
685 raise
680
686
681 def flush(self):
687 def flush(self):
682 try: self.fout.flush()
688 try: self.fout.flush()
683 except (IOError, ValueError): pass
689 except (IOError, ValueError): pass
684 try: self.ferr.flush()
690 try: self.ferr.flush()
685 except (IOError, ValueError): pass
691 except (IOError, ValueError): pass
686
692
687 def _isatty(self, fh):
693 def _isatty(self, fh):
688 if self.configbool('ui', 'nontty', False):
694 if self.configbool('ui', 'nontty', False):
689 return False
695 return False
690 return util.isatty(fh)
696 return util.isatty(fh)
691
697
692 def interactive(self):
698 def interactive(self):
693 '''is interactive input allowed?
699 '''is interactive input allowed?
694
700
695 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
696 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
697 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
698 specified.
704 specified.
699
705
700 Interactiveness is triggered by the value of the `ui.interactive'
706 Interactiveness is triggered by the value of the `ui.interactive'
701 configuration variable or - if it is unset - when `sys.stdin' points
707 configuration variable or - if it is unset - when `sys.stdin' points
702 to a terminal device.
708 to a terminal device.
703
709
704 This function refers to input only; for output, see `ui.formatted()'.
710 This function refers to input only; for output, see `ui.formatted()'.
705 '''
711 '''
706 i = self.configbool("ui", "interactive", None)
712 i = self.configbool("ui", "interactive", None)
707 if i is None:
713 if i is None:
708 # some environments replace stdin without implementing isatty
714 # some environments replace stdin without implementing isatty
709 # usually those are non-interactive
715 # usually those are non-interactive
710 return self._isatty(self.fin)
716 return self._isatty(self.fin)
711
717
712 return i
718 return i
713
719
714 def termwidth(self):
720 def termwidth(self):
715 '''how wide is the terminal in columns?
721 '''how wide is the terminal in columns?
716 '''
722 '''
717 if 'COLUMNS' in os.environ:
723 if 'COLUMNS' in os.environ:
718 try:
724 try:
719 return int(os.environ['COLUMNS'])
725 return int(os.environ['COLUMNS'])
720 except ValueError:
726 except ValueError:
721 pass
727 pass
722 return util.termwidth()
728 return util.termwidth()
723
729
724 def formatted(self):
730 def formatted(self):
725 '''should formatted output be used?
731 '''should formatted output be used?
726
732
727 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.
728 Examples of this are truncating long lines or colorizing messages.
734 Examples of this are truncating long lines or colorizing messages.
729 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
730 utilities, e.g. `grep'.
736 utilities, e.g. `grep'.
731
737
732 Formatted output is triggered by the value of the `ui.formatted'
738 Formatted output is triggered by the value of the `ui.formatted'
733 configuration variable or - if it is unset - when `sys.stdout' points
739 configuration variable or - if it is unset - when `sys.stdout' points
734 to a terminal device. Please note that `ui.formatted' should be
740 to a terminal device. Please note that `ui.formatted' should be
735 considered an implementation detail; it is not intended for use outside
741 considered an implementation detail; it is not intended for use outside
736 Mercurial or its extensions.
742 Mercurial or its extensions.
737
743
738 This function refers to output only; for input, see `ui.interactive()'.
744 This function refers to output only; for input, see `ui.interactive()'.
739 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()'.
740 '''
746 '''
741 if self.plain():
747 if self.plain():
742 return False
748 return False
743
749
744 i = self.configbool("ui", "formatted", None)
750 i = self.configbool("ui", "formatted", None)
745 if i is None:
751 if i is None:
746 # some environments replace stdout without implementing isatty
752 # some environments replace stdout without implementing isatty
747 # usually those are non-interactive
753 # usually those are non-interactive
748 return self._isatty(self.fout)
754 return self._isatty(self.fout)
749
755
750 return i
756 return i
751
757
752 def _readline(self, prompt=''):
758 def _readline(self, prompt=''):
753 if self._isatty(self.fin):
759 if self._isatty(self.fin):
754 try:
760 try:
755 # magically add command line editing support, where
761 # magically add command line editing support, where
756 # available
762 # available
757 import readline
763 import readline
758 # force demandimport to really load the module
764 # force demandimport to really load the module
759 readline.read_history_file
765 readline.read_history_file
760 # windows sometimes raises something other than ImportError
766 # windows sometimes raises something other than ImportError
761 except Exception:
767 except Exception:
762 pass
768 pass
763
769
764 # call write() so output goes through subclassed implementation
770 # call write() so output goes through subclassed implementation
765 # e.g. color extension on Windows
771 # e.g. color extension on Windows
766 self.write(prompt)
772 self.write(prompt)
767
773
768 # instead of trying to emulate raw_input, swap (self.fin,
774 # instead of trying to emulate raw_input, swap (self.fin,
769 # self.fout) with (sys.stdin, sys.stdout)
775 # self.fout) with (sys.stdin, sys.stdout)
770 oldin = sys.stdin
776 oldin = sys.stdin
771 oldout = sys.stdout
777 oldout = sys.stdout
772 sys.stdin = self.fin
778 sys.stdin = self.fin
773 sys.stdout = self.fout
779 sys.stdout = self.fout
774 # prompt ' ' must exist; otherwise readline may delete entire line
780 # prompt ' ' must exist; otherwise readline may delete entire line
775 # - http://bugs.python.org/issue12833
781 # - http://bugs.python.org/issue12833
776 line = raw_input(' ')
782 line = raw_input(' ')
777 sys.stdin = oldin
783 sys.stdin = oldin
778 sys.stdout = oldout
784 sys.stdout = oldout
779
785
780 # When stdin is in binary mode on Windows, it can cause
786 # When stdin is in binary mode on Windows, it can cause
781 # raw_input() to emit an extra trailing carriage return
787 # raw_input() to emit an extra trailing carriage return
782 if os.linesep == '\r\n' and line and line[-1] == '\r':
788 if os.linesep == '\r\n' and line and line[-1] == '\r':
783 line = line[:-1]
789 line = line[:-1]
784 return line
790 return line
785
791
786 def prompt(self, msg, default="y"):
792 def prompt(self, msg, default="y"):
787 """Prompt user with msg, read response.
793 """Prompt user with msg, read response.
788 If ui is not interactive, the default is returned.
794 If ui is not interactive, the default is returned.
789 """
795 """
790 if not self.interactive():
796 if not self.interactive():
791 self.write(msg, ' ', default, "\n")
797 self.write(msg, ' ', default, "\n")
792 return default
798 return default
793 try:
799 try:
794 r = self._readline(self.label(msg, 'ui.prompt'))
800 r = self._readline(self.label(msg, 'ui.prompt'))
795 if not r:
801 if not r:
796 r = default
802 r = default
797 if self.configbool('ui', 'promptecho'):
803 if self.configbool('ui', 'promptecho'):
798 self.write(r, "\n")
804 self.write(r, "\n")
799 return r
805 return r
800 except EOFError:
806 except EOFError:
801 raise error.ResponseExpected()
807 raise error.ResponseExpected()
802
808
803 @staticmethod
809 @staticmethod
804 def extractchoices(prompt):
810 def extractchoices(prompt):
805 """Extract prompt message and list of choices from specified prompt.
811 """Extract prompt message and list of choices from specified prompt.
806
812
807 This returns tuple "(message, choices)", and "choices" is the
813 This returns tuple "(message, choices)", and "choices" is the
808 list of tuple "(response character, text without &)".
814 list of tuple "(response character, text without &)".
809 """
815 """
810 parts = prompt.split('$$')
816 parts = prompt.split('$$')
811 msg = parts[0].rstrip(' ')
817 msg = parts[0].rstrip(' ')
812 choices = [p.strip(' ') for p in parts[1:]]
818 choices = [p.strip(' ') for p in parts[1:]]
813 return (msg,
819 return (msg,
814 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
820 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
815 for s in choices])
821 for s in choices])
816
822
817 def promptchoice(self, prompt, default=0):
823 def promptchoice(self, prompt, default=0):
818 """Prompt user with a message, read response, and ensure it matches
824 """Prompt user with a message, read response, and ensure it matches
819 one of the provided choices. The prompt is formatted as follows:
825 one of the provided choices. The prompt is formatted as follows:
820
826
821 "would you like fries with that (Yn)? $$ &Yes $$ &No"
827 "would you like fries with that (Yn)? $$ &Yes $$ &No"
822
828
823 The index of the choice is returned. Responses are case
829 The index of the choice is returned. Responses are case
824 insensitive. If ui is not interactive, the default is
830 insensitive. If ui is not interactive, the default is
825 returned.
831 returned.
826 """
832 """
827
833
828 msg, choices = self.extractchoices(prompt)
834 msg, choices = self.extractchoices(prompt)
829 resps = [r for r, t in choices]
835 resps = [r for r, t in choices]
830 while True:
836 while True:
831 r = self.prompt(msg, resps[default])
837 r = self.prompt(msg, resps[default])
832 if r.lower() in resps:
838 if r.lower() in resps:
833 return resps.index(r.lower())
839 return resps.index(r.lower())
834 self.write(_("unrecognized response\n"))
840 self.write(_("unrecognized response\n"))
835
841
836 def getpass(self, prompt=None, default=None):
842 def getpass(self, prompt=None, default=None):
837 if not self.interactive():
843 if not self.interactive():
838 return default
844 return default
839 try:
845 try:
840 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
846 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
841 # disable getpass() only if explicitly specified. it's still valid
847 # disable getpass() only if explicitly specified. it's still valid
842 # to interact with tty even if fin is not a tty.
848 # to interact with tty even if fin is not a tty.
843 if self.configbool('ui', 'nontty'):
849 if self.configbool('ui', 'nontty'):
844 return self.fin.readline().rstrip('\n')
850 return self.fin.readline().rstrip('\n')
845 else:
851 else:
846 return getpass.getpass('')
852 return getpass.getpass('')
847 except EOFError:
853 except EOFError:
848 raise error.ResponseExpected()
854 raise error.ResponseExpected()
849 def status(self, *msg, **opts):
855 def status(self, *msg, **opts):
850 '''write status message to output (if ui.quiet is False)
856 '''write status message to output (if ui.quiet is False)
851
857
852 This adds an output label of "ui.status".
858 This adds an output label of "ui.status".
853 '''
859 '''
854 if not self.quiet:
860 if not self.quiet:
855 opts['label'] = opts.get('label', '') + ' ui.status'
861 opts['label'] = opts.get('label', '') + ' ui.status'
856 self.write(*msg, **opts)
862 self.write(*msg, **opts)
857 def warn(self, *msg, **opts):
863 def warn(self, *msg, **opts):
858 '''write warning message to output (stderr)
864 '''write warning message to output (stderr)
859
865
860 This adds an output label of "ui.warning".
866 This adds an output label of "ui.warning".
861 '''
867 '''
862 opts['label'] = opts.get('label', '') + ' ui.warning'
868 opts['label'] = opts.get('label', '') + ' ui.warning'
863 self.write_err(*msg, **opts)
869 self.write_err(*msg, **opts)
864 def note(self, *msg, **opts):
870 def note(self, *msg, **opts):
865 '''write note to output (if ui.verbose is True)
871 '''write note to output (if ui.verbose is True)
866
872
867 This adds an output label of "ui.note".
873 This adds an output label of "ui.note".
868 '''
874 '''
869 if self.verbose:
875 if self.verbose:
870 opts['label'] = opts.get('label', '') + ' ui.note'
876 opts['label'] = opts.get('label', '') + ' ui.note'
871 self.write(*msg, **opts)
877 self.write(*msg, **opts)
872 def debug(self, *msg, **opts):
878 def debug(self, *msg, **opts):
873 '''write debug message to output (if ui.debugflag is True)
879 '''write debug message to output (if ui.debugflag is True)
874
880
875 This adds an output label of "ui.debug".
881 This adds an output label of "ui.debug".
876 '''
882 '''
877 if self.debugflag:
883 if self.debugflag:
878 opts['label'] = opts.get('label', '') + ' ui.debug'
884 opts['label'] = opts.get('label', '') + ' ui.debug'
879 self.write(*msg, **opts)
885 self.write(*msg, **opts)
880
886
881 def edit(self, text, user, extra=None, editform=None, pending=None):
887 def edit(self, text, user, extra=None, editform=None, pending=None):
882 extra_defaults = { 'prefix': 'editor' }
888 extra_defaults = { 'prefix': 'editor' }
883 if extra is not None:
889 if extra is not None:
884 extra_defaults.update(extra)
890 extra_defaults.update(extra)
885 extra = extra_defaults
891 extra = extra_defaults
886 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
892 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
887 suffix=".txt", text=True)
893 suffix=".txt", text=True)
888 try:
894 try:
889 f = os.fdopen(fd, "w")
895 f = os.fdopen(fd, "w")
890 f.write(text)
896 f.write(text)
891 f.close()
897 f.close()
892
898
893 environ = {'HGUSER': user}
899 environ = {'HGUSER': user}
894 if 'transplant_source' in extra:
900 if 'transplant_source' in extra:
895 environ.update({'HGREVISION': hex(extra['transplant_source'])})
901 environ.update({'HGREVISION': hex(extra['transplant_source'])})
896 for label in ('intermediate-source', 'source', 'rebase_source'):
902 for label in ('intermediate-source', 'source', 'rebase_source'):
897 if label in extra:
903 if label in extra:
898 environ.update({'HGREVISION': extra[label]})
904 environ.update({'HGREVISION': extra[label]})
899 break
905 break
900 if editform:
906 if editform:
901 environ.update({'HGEDITFORM': editform})
907 environ.update({'HGEDITFORM': editform})
902 if pending:
908 if pending:
903 environ.update({'HG_PENDING': pending})
909 environ.update({'HG_PENDING': pending})
904
910
905 editor = self.geteditor()
911 editor = self.geteditor()
906
912
907 self.system("%s \"%s\"" % (editor, name),
913 self.system("%s \"%s\"" % (editor, name),
908 environ=environ,
914 environ=environ,
909 onerr=error.Abort, errprefix=_("edit failed"))
915 onerr=error.Abort, errprefix=_("edit failed"))
910
916
911 f = open(name)
917 f = open(name)
912 t = f.read()
918 t = f.read()
913 f.close()
919 f.close()
914 finally:
920 finally:
915 os.unlink(name)
921 os.unlink(name)
916
922
917 return t
923 return t
918
924
919 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):
920 '''execute shell command with appropriate output stream. command
926 '''execute shell command with appropriate output stream. command
921 output will be redirected if fout is not stdout.
927 output will be redirected if fout is not stdout.
922 '''
928 '''
923 out = self.fout
929 out = self.fout
924 if any(s[1] for s in self._bufferstates):
930 if any(s[1] for s in self._bufferstates):
925 out = self
931 out = self
926 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
932 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
927 errprefix=errprefix, out=out)
933 errprefix=errprefix, out=out)
928
934
929 def traceback(self, exc=None, force=False):
935 def traceback(self, exc=None, force=False):
930 '''print exception traceback if traceback printing enabled or forced.
936 '''print exception traceback if traceback printing enabled or forced.
931 only to call in exception handler. returns true if traceback
937 only to call in exception handler. returns true if traceback
932 printed.'''
938 printed.'''
933 if self.tracebackflag or force:
939 if self.tracebackflag or force:
934 if exc is None:
940 if exc is None:
935 exc = sys.exc_info()
941 exc = sys.exc_info()
936 cause = getattr(exc[1], 'cause', None)
942 cause = getattr(exc[1], 'cause', None)
937
943
938 if cause is not None:
944 if cause is not None:
939 causetb = traceback.format_tb(cause[2])
945 causetb = traceback.format_tb(cause[2])
940 exctb = traceback.format_tb(exc[2])
946 exctb = traceback.format_tb(exc[2])
941 exconly = traceback.format_exception_only(cause[0], cause[1])
947 exconly = traceback.format_exception_only(cause[0], cause[1])
942
948
943 # exclude frame where 'exc' was chained and rethrown from exctb
949 # exclude frame where 'exc' was chained and rethrown from exctb
944 self.write_err('Traceback (most recent call last):\n',
950 self.write_err('Traceback (most recent call last):\n',
945 ''.join(exctb[:-1]),
951 ''.join(exctb[:-1]),
946 ''.join(causetb),
952 ''.join(causetb),
947 ''.join(exconly))
953 ''.join(exconly))
948 else:
954 else:
949 output = traceback.format_exception(exc[0], exc[1], exc[2])
955 output = traceback.format_exception(exc[0], exc[1], exc[2])
950 self.write_err(''.join(output))
956 self.write_err(''.join(output))
951 return self.tracebackflag or force
957 return self.tracebackflag or force
952
958
953 def geteditor(self):
959 def geteditor(self):
954 '''return editor to use'''
960 '''return editor to use'''
955 if sys.platform == 'plan9':
961 if sys.platform == 'plan9':
956 # vi is the MIPS instruction simulator on Plan 9. We
962 # vi is the MIPS instruction simulator on Plan 9. We
957 # instead default to E to plumb commit messages to
963 # instead default to E to plumb commit messages to
958 # avoid confusion.
964 # avoid confusion.
959 editor = 'E'
965 editor = 'E'
960 else:
966 else:
961 editor = 'vi'
967 editor = 'vi'
962 return (os.environ.get("HGEDITOR") or
968 return (os.environ.get("HGEDITOR") or
963 self.config("ui", "editor") or
969 self.config("ui", "editor") or
964 os.environ.get("VISUAL") or
970 os.environ.get("VISUAL") or
965 os.environ.get("EDITOR", editor))
971 os.environ.get("EDITOR", editor))
966
972
967 @util.propertycache
973 @util.propertycache
968 def _progbar(self):
974 def _progbar(self):
969 """setup the progbar singleton to the ui object"""
975 """setup the progbar singleton to the ui object"""
970 if (self.quiet or self.debugflag
976 if (self.quiet or self.debugflag
971 or self.configbool('progress', 'disable', False)
977 or self.configbool('progress', 'disable', False)
972 or not progress.shouldprint(self)):
978 or not progress.shouldprint(self)):
973 return None
979 return None
974 return getprogbar(self)
980 return getprogbar(self)
975
981
976 def _progclear(self):
982 def _progclear(self):
977 """clear progress bar output if any. use it before any output"""
983 """clear progress bar output if any. use it before any output"""
978 if '_progbar' not in vars(self): # nothing loaded yet
984 if '_progbar' not in vars(self): # nothing loaded yet
979 return
985 return
980 if self._progbar is not None and self._progbar.printed:
986 if self._progbar is not None and self._progbar.printed:
981 self._progbar.clear()
987 self._progbar.clear()
982
988
983 def progress(self, topic, pos, item="", unit="", total=None):
989 def progress(self, topic, pos, item="", unit="", total=None):
984 '''show a progress message
990 '''show a progress message
985
991
986 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
987 by default, but with extensions or GUI tools it may be
993 by default, but with extensions or GUI tools it may be
988 visible. 'topic' is the current operation, 'item' is a
994 visible. 'topic' is the current operation, 'item' is a
989 non-numeric marker of the current position (i.e. the currently
995 non-numeric marker of the current position (i.e. the currently
990 in-process file), 'pos' is the current numeric position (i.e.
996 in-process file), 'pos' is the current numeric position (i.e.
991 revision, bytes, etc.), unit is a corresponding unit label,
997 revision, bytes, etc.), unit is a corresponding unit label,
992 and total is the highest expected pos.
998 and total is the highest expected pos.
993
999
994 Multiple nested topics may be active at a time.
1000 Multiple nested topics may be active at a time.
995
1001
996 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
997 termination.
1003 termination.
998 '''
1004 '''
999 if self._progbar is not None:
1005 if self._progbar is not None:
1000 self._progbar.progress(topic, pos, item=item, unit=unit,
1006 self._progbar.progress(topic, pos, item=item, unit=unit,
1001 total=total)
1007 total=total)
1002 if pos is None or not self.configbool('progress', 'debug'):
1008 if pos is None or not self.configbool('progress', 'debug'):
1003 return
1009 return
1004
1010
1005 if unit:
1011 if unit:
1006 unit = ' ' + unit
1012 unit = ' ' + unit
1007 if item:
1013 if item:
1008 item = ' ' + item
1014 item = ' ' + item
1009
1015
1010 if total:
1016 if total:
1011 pct = 100.0 * pos / total
1017 pct = 100.0 * pos / total
1012 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1018 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1013 % (topic, item, pos, total, unit, pct))
1019 % (topic, item, pos, total, unit, pct))
1014 else:
1020 else:
1015 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1021 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1016
1022
1017 def log(self, service, *msg, **opts):
1023 def log(self, service, *msg, **opts):
1018 '''hook for logging facility extensions
1024 '''hook for logging facility extensions
1019
1025
1020 service should be a readily-identifiable subsystem, which will
1026 service should be a readily-identifiable subsystem, which will
1021 allow filtering.
1027 allow filtering.
1022
1028
1023 *msg should be a newline-terminated format string to log, and
1029 *msg should be a newline-terminated format string to log, and
1024 then any values to %-format into that format string.
1030 then any values to %-format into that format string.
1025
1031
1026 **opts currently has no defined meanings.
1032 **opts currently has no defined meanings.
1027 '''
1033 '''
1028
1034
1029 def label(self, msg, label):
1035 def label(self, msg, label):
1030 '''style msg based on supplied label
1036 '''style msg based on supplied label
1031
1037
1032 Like ui.write(), this just returns msg unchanged, but extensions
1038 Like ui.write(), this just returns msg unchanged, but extensions
1033 and GUI tools can override it to allow styling output without
1039 and GUI tools can override it to allow styling output without
1034 writing it.
1040 writing it.
1035
1041
1036 ui.write(s, 'label') is equivalent to
1042 ui.write(s, 'label') is equivalent to
1037 ui.write(ui.label(s, 'label')).
1043 ui.write(ui.label(s, 'label')).
1038 '''
1044 '''
1039 return msg
1045 return msg
1040
1046
1041 def develwarn(self, msg):
1047 def develwarn(self, msg):
1042 """issue a developer warning message"""
1048 """issue a developer warning message"""
1043 msg = 'devel-warn: ' + msg
1049 msg = 'devel-warn: ' + msg
1044 if self.tracebackflag:
1050 if self.tracebackflag:
1045 util.debugstacktrace(msg, 2, self.ferr, self.fout)
1051 util.debugstacktrace(msg, 2, self.ferr, self.fout)
1046 else:
1052 else:
1047 curframe = inspect.currentframe()
1053 curframe = inspect.currentframe()
1048 calframe = inspect.getouterframes(curframe, 2)
1054 calframe = inspect.getouterframes(curframe, 2)
1049 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1055 self.write_err('%s at: %s:%s (%s)\n' % ((msg,) + calframe[2][1:4]))
1050
1056
1051 class paths(dict):
1057 class paths(dict):
1052 """Represents a collection of paths and their configs.
1058 """Represents a collection of paths and their configs.
1053
1059
1054 Data is initially derived from ui instances and the config files they have
1060 Data is initially derived from ui instances and the config files they have
1055 loaded.
1061 loaded.
1056 """
1062 """
1057 def __init__(self, ui):
1063 def __init__(self, ui):
1058 dict.__init__(self)
1064 dict.__init__(self)
1059
1065
1060 for name, loc in ui.configitems('paths'):
1066 for name, loc in ui.configitems('paths'):
1061 # No location is the same as not existing.
1067 # No location is the same as not existing.
1062 if not loc:
1068 if not loc:
1063 continue
1069 continue
1064
1070
1065 # TODO ignore default-push once all consumers stop referencing it
1071 # TODO ignore default-push once all consumers stop referencing it
1066 # since it is handled specifically below.
1072 # since it is handled specifically below.
1067
1073
1068 self[name] = path(name, rawloc=loc)
1074 self[name] = path(name, rawloc=loc)
1069
1075
1070 # Handle default-push, which is a one-off that defines the push URL for
1076 # Handle default-push, which is a one-off that defines the push URL for
1071 # the "default" path.
1077 # the "default" path.
1072 defaultpush = ui.config('paths', 'default-push')
1078 defaultpush = ui.config('paths', 'default-push')
1073 if defaultpush:
1079 if defaultpush:
1074 # "default-push" can be defined without "default" entry. This is a
1080 # "default-push" can be defined without "default" entry. This is a
1075 # bit weird, but is allowed for backwards compatibility.
1081 # bit weird, but is allowed for backwards compatibility.
1076 if 'default' not in self:
1082 if 'default' not in self:
1077 self['default'] = path('default', rawloc=defaultpush)
1083 self['default'] = path('default', rawloc=defaultpush)
1078 self['default']._pushloc = defaultpush
1084 self['default']._pushloc = defaultpush
1079
1085
1080 def getpath(self, name, default=None):
1086 def getpath(self, name, default=None):
1081 """Return a ``path`` from a string, falling back to a default.
1087 """Return a ``path`` from a string, falling back to a default.
1082
1088
1083 ``name`` can be a named path or locations. Locations are filesystem
1089 ``name`` can be a named path or locations. Locations are filesystem
1084 paths or URIs.
1090 paths or URIs.
1085
1091
1086 Returns None if ``name`` is not a registered path, a URI, or a local
1092 Returns None if ``name`` is not a registered path, a URI, or a local
1087 path to a repo.
1093 path to a repo.
1088 """
1094 """
1089 # Only fall back to default if no path was requested.
1095 # Only fall back to default if no path was requested.
1090 if name is None:
1096 if name is None:
1091 if default:
1097 if default:
1092 try:
1098 try:
1093 return self[default]
1099 return self[default]
1094 except KeyError:
1100 except KeyError:
1095 return None
1101 return None
1096 else:
1102 else:
1097 return None
1103 return None
1098
1104
1099 # Most likely empty string.
1105 # Most likely empty string.
1100 # This may need to raise in the future.
1106 # This may need to raise in the future.
1101 if not name:
1107 if not name:
1102 return None
1108 return None
1103
1109
1104 try:
1110 try:
1105 return self[name]
1111 return self[name]
1106 except KeyError:
1112 except KeyError:
1107 # Try to resolve as a local path or URI.
1113 # Try to resolve as a local path or URI.
1108 try:
1114 try:
1109 return path(None, rawloc=name)
1115 return path(None, rawloc=name)
1110 except ValueError:
1116 except ValueError:
1111 raise error.RepoError(_('repository %s does not exist') %
1117 raise error.RepoError(_('repository %s does not exist') %
1112 name)
1118 name)
1113
1119
1114 assert False
1120 assert False
1115
1121
1116 class path(object):
1122 class path(object):
1117 """Represents an individual path and its configuration."""
1123 """Represents an individual path and its configuration."""
1118
1124
1119 def __init__(self, name, rawloc=None, pushloc=None):
1125 def __init__(self, name, rawloc=None, pushloc=None):
1120 """Construct a path from its config options.
1126 """Construct a path from its config options.
1121
1127
1122 ``name`` is the symbolic name of the path.
1128 ``name`` is the symbolic name of the path.
1123 ``rawloc`` is the raw location, as defined in the config.
1129 ``rawloc`` is the raw location, as defined in the config.
1124 ``pushloc`` is the raw locations pushes should be made to.
1130 ``pushloc`` is the raw locations pushes should be made to.
1125
1131
1126 If ``name`` is not defined, we require that the location be a) a local
1132 If ``name`` is not defined, we require that the location be a) a local
1127 filesystem path with a .hg directory or b) a URL. If not,
1133 filesystem path with a .hg directory or b) a URL. If not,
1128 ``ValueError`` is raised.
1134 ``ValueError`` is raised.
1129 """
1135 """
1130 if not rawloc:
1136 if not rawloc:
1131 raise ValueError('rawloc must be defined')
1137 raise ValueError('rawloc must be defined')
1132
1138
1133 # Locations may define branches via syntax <base>#<branch>.
1139 # Locations may define branches via syntax <base>#<branch>.
1134 u = util.url(rawloc)
1140 u = util.url(rawloc)
1135 branch = None
1141 branch = None
1136 if u.fragment:
1142 if u.fragment:
1137 branch = u.fragment
1143 branch = u.fragment
1138 u.fragment = None
1144 u.fragment = None
1139
1145
1140 self.url = u
1146 self.url = u
1141 self.branch = branch
1147 self.branch = branch
1142
1148
1143 self.name = name
1149 self.name = name
1144 self.rawloc = rawloc
1150 self.rawloc = rawloc
1145 self.loc = str(u)
1151 self.loc = str(u)
1146 self._pushloc = pushloc
1152 self._pushloc = pushloc
1147
1153
1148 # When given a raw location but not a symbolic name, validate the
1154 # When given a raw location but not a symbolic name, validate the
1149 # location is valid.
1155 # location is valid.
1150 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1156 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1151 raise ValueError('location is not a URL or path to a local '
1157 raise ValueError('location is not a URL or path to a local '
1152 'repo: %s' % rawloc)
1158 'repo: %s' % rawloc)
1153
1159
1154 def _isvalidlocalpath(self, path):
1160 def _isvalidlocalpath(self, path):
1155 """Returns True if the given path is a potentially valid repository.
1161 """Returns True if the given path is a potentially valid repository.
1156 This is its own function so that extensions can change the definition of
1162 This is its own function so that extensions can change the definition of
1157 'valid' in this case (like when pulling from a git repo into a hg
1163 'valid' in this case (like when pulling from a git repo into a hg
1158 one)."""
1164 one)."""
1159 return os.path.isdir(os.path.join(path, '.hg'))
1165 return os.path.isdir(os.path.join(path, '.hg'))
1160
1166
1161 @property
1167 @property
1162 def pushloc(self):
1168 def pushloc(self):
1163 return self._pushloc or self.loc
1169 return self._pushloc or self.loc
1164
1170
1165 # we instantiate one globally shared progress bar to avoid
1171 # we instantiate one globally shared progress bar to avoid
1166 # competing progress bars when multiple UI objects get created
1172 # competing progress bars when multiple UI objects get created
1167 _progresssingleton = None
1173 _progresssingleton = None
1168
1174
1169 def getprogbar(ui):
1175 def getprogbar(ui):
1170 global _progresssingleton
1176 global _progresssingleton
1171 if _progresssingleton is None:
1177 if _progresssingleton is None:
1172 # passing 'ui' object to the singleton is fishy,
1178 # passing 'ui' object to the singleton is fishy,
1173 # this is how the extension used to work but feel free to rework it.
1179 # this is how the extension used to work but feel free to rework it.
1174 _progresssingleton = progress.progbar(ui)
1180 _progresssingleton = progress.progbar(ui)
1175 return _progresssingleton
1181 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now