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