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