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