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