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