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