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