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