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