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