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