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