##// END OF EJS Templates
ui: refactor extractchoices so it doesn't break on Python 3...
Augie Fackler -
r33680:d880a6bc default
parent child Browse files
Show More
@@ -1,1777 +1,1778 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 b"""# example user config (see 'hg help config' for more info)
63 b"""# 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 b"""# example repository config (see 'hg help config' for more info)
85 b"""# 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 b"""# example repository config (see 'hg help config' for more info)
102 b"""# 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 b"""# example system-wide hg config (see 'hg help config' for more info)
118 b"""# 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 or command in self.configlist('pager', 'ignore')
949 or command in self.configlist('pager', 'ignore')
950 or not self.configbool('ui', 'paginate')
950 or not self.configbool('ui', 'paginate')
951 or not self.configbool('pager', 'attend-' + command, True)
951 or not self.configbool('pager', 'attend-' + command, True)
952 # TODO: if we want to allow HGPLAINEXCEPT=pager,
952 # TODO: if we want to allow HGPLAINEXCEPT=pager,
953 # formatted() will need some adjustment.
953 # formatted() will need some adjustment.
954 or not self.formatted()
954 or not self.formatted()
955 or self.plain()
955 or self.plain()
956 # TODO: expose debugger-enabled on the UI object
956 # TODO: expose debugger-enabled on the UI object
957 or '--debugger' in pycompat.sysargv):
957 or '--debugger' in pycompat.sysargv):
958 # We only want to paginate if the ui appears to be
958 # We only want to paginate if the ui appears to be
959 # interactive, the user didn't say HGPLAIN or
959 # interactive, the user didn't say HGPLAIN or
960 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
960 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
961 return
961 return
962
962
963 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
963 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
964 if not pagercmd:
964 if not pagercmd:
965 return
965 return
966
966
967 pagerenv = {}
967 pagerenv = {}
968 for name, value in rcutil.defaultpagerenv().items():
968 for name, value in rcutil.defaultpagerenv().items():
969 if name not in encoding.environ:
969 if name not in encoding.environ:
970 pagerenv[name] = value
970 pagerenv[name] = value
971
971
972 self.debug('starting pager for command %r\n' % command)
972 self.debug('starting pager for command %r\n' % command)
973 self.flush()
973 self.flush()
974
974
975 wasformatted = self.formatted()
975 wasformatted = self.formatted()
976 if util.safehasattr(signal, "SIGPIPE"):
976 if util.safehasattr(signal, "SIGPIPE"):
977 signal.signal(signal.SIGPIPE, _catchterm)
977 signal.signal(signal.SIGPIPE, _catchterm)
978 if self._runpager(pagercmd, pagerenv):
978 if self._runpager(pagercmd, pagerenv):
979 self.pageractive = True
979 self.pageractive = True
980 # Preserve the formatted-ness of the UI. This is important
980 # Preserve the formatted-ness of the UI. This is important
981 # because we mess with stdout, which might confuse
981 # because we mess with stdout, which might confuse
982 # auto-detection of things being formatted.
982 # auto-detection of things being formatted.
983 self.setconfig('ui', 'formatted', wasformatted, 'pager')
983 self.setconfig('ui', 'formatted', wasformatted, 'pager')
984 self.setconfig('ui', 'interactive', False, 'pager')
984 self.setconfig('ui', 'interactive', False, 'pager')
985
985
986 # If pagermode differs from color.mode, reconfigure color now that
986 # If pagermode differs from color.mode, reconfigure color now that
987 # pageractive is set.
987 # pageractive is set.
988 cm = self._colormode
988 cm = self._colormode
989 if cm != self.config('color', 'pagermode', cm):
989 if cm != self.config('color', 'pagermode', cm):
990 color.setup(self)
990 color.setup(self)
991 else:
991 else:
992 # If the pager can't be spawned in dispatch when --pager=on is
992 # If the pager can't be spawned in dispatch when --pager=on is
993 # given, don't try again when the command runs, to avoid a duplicate
993 # given, don't try again when the command runs, to avoid a duplicate
994 # warning about a missing pager command.
994 # warning about a missing pager command.
995 self.disablepager()
995 self.disablepager()
996
996
997 def _runpager(self, command, env=None):
997 def _runpager(self, command, env=None):
998 """Actually start the pager and set up file descriptors.
998 """Actually start the pager and set up file descriptors.
999
999
1000 This is separate in part so that extensions (like chg) can
1000 This is separate in part so that extensions (like chg) can
1001 override how a pager is invoked.
1001 override how a pager is invoked.
1002 """
1002 """
1003 if command == 'cat':
1003 if command == 'cat':
1004 # Save ourselves some work.
1004 # Save ourselves some work.
1005 return False
1005 return False
1006 # If the command doesn't contain any of these characters, we
1006 # If the command doesn't contain any of these characters, we
1007 # assume it's a binary and exec it directly. This means for
1007 # assume it's a binary and exec it directly. This means for
1008 # simple pager command configurations, we can degrade
1008 # simple pager command configurations, we can degrade
1009 # gracefully and tell the user about their broken pager.
1009 # gracefully and tell the user about their broken pager.
1010 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1010 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1011
1011
1012 if pycompat.osname == 'nt' and not shell:
1012 if pycompat.osname == 'nt' and not shell:
1013 # Window's built-in `more` cannot be invoked with shell=False, but
1013 # Window's built-in `more` cannot be invoked with shell=False, but
1014 # its `more.com` can. Hide this implementation detail from the
1014 # its `more.com` can. Hide this implementation detail from the
1015 # user so we can also get sane bad PAGER behavior. MSYS has
1015 # user so we can also get sane bad PAGER behavior. MSYS has
1016 # `more.exe`, so do a cmd.exe style resolution of the executable to
1016 # `more.exe`, so do a cmd.exe style resolution of the executable to
1017 # determine which one to use.
1017 # determine which one to use.
1018 fullcmd = util.findexe(command)
1018 fullcmd = util.findexe(command)
1019 if not fullcmd:
1019 if not fullcmd:
1020 self.warn(_("missing pager command '%s', skipping pager\n")
1020 self.warn(_("missing pager command '%s', skipping pager\n")
1021 % command)
1021 % command)
1022 return False
1022 return False
1023
1023
1024 command = fullcmd
1024 command = fullcmd
1025
1025
1026 try:
1026 try:
1027 pager = subprocess.Popen(
1027 pager = subprocess.Popen(
1028 command, shell=shell, bufsize=-1,
1028 command, shell=shell, bufsize=-1,
1029 close_fds=util.closefds, stdin=subprocess.PIPE,
1029 close_fds=util.closefds, stdin=subprocess.PIPE,
1030 stdout=util.stdout, stderr=util.stderr,
1030 stdout=util.stdout, stderr=util.stderr,
1031 env=util.shellenviron(env))
1031 env=util.shellenviron(env))
1032 except OSError as e:
1032 except OSError as e:
1033 if e.errno == errno.ENOENT and not shell:
1033 if e.errno == errno.ENOENT and not shell:
1034 self.warn(_("missing pager command '%s', skipping pager\n")
1034 self.warn(_("missing pager command '%s', skipping pager\n")
1035 % command)
1035 % command)
1036 return False
1036 return False
1037 raise
1037 raise
1038
1038
1039 # back up original file descriptors
1039 # back up original file descriptors
1040 stdoutfd = os.dup(util.stdout.fileno())
1040 stdoutfd = os.dup(util.stdout.fileno())
1041 stderrfd = os.dup(util.stderr.fileno())
1041 stderrfd = os.dup(util.stderr.fileno())
1042
1042
1043 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1043 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1044 if self._isatty(util.stderr):
1044 if self._isatty(util.stderr):
1045 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1045 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1046
1046
1047 @self.atexit
1047 @self.atexit
1048 def killpager():
1048 def killpager():
1049 if util.safehasattr(signal, "SIGINT"):
1049 if util.safehasattr(signal, "SIGINT"):
1050 signal.signal(signal.SIGINT, signal.SIG_IGN)
1050 signal.signal(signal.SIGINT, signal.SIG_IGN)
1051 # restore original fds, closing pager.stdin copies in the process
1051 # restore original fds, closing pager.stdin copies in the process
1052 os.dup2(stdoutfd, util.stdout.fileno())
1052 os.dup2(stdoutfd, util.stdout.fileno())
1053 os.dup2(stderrfd, util.stderr.fileno())
1053 os.dup2(stderrfd, util.stderr.fileno())
1054 pager.stdin.close()
1054 pager.stdin.close()
1055 pager.wait()
1055 pager.wait()
1056
1056
1057 return True
1057 return True
1058
1058
1059 def atexit(self, func, *args, **kwargs):
1059 def atexit(self, func, *args, **kwargs):
1060 '''register a function to run after dispatching a request
1060 '''register a function to run after dispatching a request
1061
1061
1062 Handlers do not stay registered across request boundaries.'''
1062 Handlers do not stay registered across request boundaries.'''
1063 self._exithandlers.append((func, args, kwargs))
1063 self._exithandlers.append((func, args, kwargs))
1064 return func
1064 return func
1065
1065
1066 def interface(self, feature):
1066 def interface(self, feature):
1067 """what interface to use for interactive console features?
1067 """what interface to use for interactive console features?
1068
1068
1069 The interface is controlled by the value of `ui.interface` but also by
1069 The interface is controlled by the value of `ui.interface` but also by
1070 the value of feature-specific configuration. For example:
1070 the value of feature-specific configuration. For example:
1071
1071
1072 ui.interface.histedit = text
1072 ui.interface.histedit = text
1073 ui.interface.chunkselector = curses
1073 ui.interface.chunkselector = curses
1074
1074
1075 Here the features are "histedit" and "chunkselector".
1075 Here the features are "histedit" and "chunkselector".
1076
1076
1077 The configuration above means that the default interfaces for commands
1077 The configuration above means that the default interfaces for commands
1078 is curses, the interface for histedit is text and the interface for
1078 is curses, the interface for histedit is text and the interface for
1079 selecting chunk is crecord (the best curses interface available).
1079 selecting chunk is crecord (the best curses interface available).
1080
1080
1081 Consider the following example:
1081 Consider the following example:
1082 ui.interface = curses
1082 ui.interface = curses
1083 ui.interface.histedit = text
1083 ui.interface.histedit = text
1084
1084
1085 Then histedit will use the text interface and chunkselector will use
1085 Then histedit will use the text interface and chunkselector will use
1086 the default curses interface (crecord at the moment).
1086 the default curses interface (crecord at the moment).
1087 """
1087 """
1088 alldefaults = frozenset(["text", "curses"])
1088 alldefaults = frozenset(["text", "curses"])
1089
1089
1090 featureinterfaces = {
1090 featureinterfaces = {
1091 "chunkselector": [
1091 "chunkselector": [
1092 "text",
1092 "text",
1093 "curses",
1093 "curses",
1094 ]
1094 ]
1095 }
1095 }
1096
1096
1097 # Feature-specific interface
1097 # Feature-specific interface
1098 if feature not in featureinterfaces.keys():
1098 if feature not in featureinterfaces.keys():
1099 # Programming error, not user error
1099 # Programming error, not user error
1100 raise ValueError("Unknown feature requested %s" % feature)
1100 raise ValueError("Unknown feature requested %s" % feature)
1101
1101
1102 availableinterfaces = frozenset(featureinterfaces[feature])
1102 availableinterfaces = frozenset(featureinterfaces[feature])
1103 if alldefaults > availableinterfaces:
1103 if alldefaults > availableinterfaces:
1104 # Programming error, not user error. We need a use case to
1104 # Programming error, not user error. We need a use case to
1105 # define the right thing to do here.
1105 # define the right thing to do here.
1106 raise ValueError(
1106 raise ValueError(
1107 "Feature %s does not handle all default interfaces" %
1107 "Feature %s does not handle all default interfaces" %
1108 feature)
1108 feature)
1109
1109
1110 if self.plain():
1110 if self.plain():
1111 return "text"
1111 return "text"
1112
1112
1113 # Default interface for all the features
1113 # Default interface for all the features
1114 defaultinterface = "text"
1114 defaultinterface = "text"
1115 i = self.config("ui", "interface")
1115 i = self.config("ui", "interface")
1116 if i in alldefaults:
1116 if i in alldefaults:
1117 defaultinterface = i
1117 defaultinterface = i
1118
1118
1119 choseninterface = defaultinterface
1119 choseninterface = defaultinterface
1120 f = self.config("ui", "interface.%s" % feature, None)
1120 f = self.config("ui", "interface.%s" % feature, None)
1121 if f in availableinterfaces:
1121 if f in availableinterfaces:
1122 choseninterface = f
1122 choseninterface = f
1123
1123
1124 if i is not None and defaultinterface != i:
1124 if i is not None and defaultinterface != i:
1125 if f is not None:
1125 if f is not None:
1126 self.warn(_("invalid value for ui.interface: %s\n") %
1126 self.warn(_("invalid value for ui.interface: %s\n") %
1127 (i,))
1127 (i,))
1128 else:
1128 else:
1129 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1129 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1130 (i, choseninterface))
1130 (i, choseninterface))
1131 if f is not None and choseninterface != f:
1131 if f is not None and choseninterface != f:
1132 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1132 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1133 (feature, f, choseninterface))
1133 (feature, f, choseninterface))
1134
1134
1135 return choseninterface
1135 return choseninterface
1136
1136
1137 def interactive(self):
1137 def interactive(self):
1138 '''is interactive input allowed?
1138 '''is interactive input allowed?
1139
1139
1140 An interactive session is a session where input can be reasonably read
1140 An interactive session is a session where input can be reasonably read
1141 from `sys.stdin'. If this function returns false, any attempt to read
1141 from `sys.stdin'. If this function returns false, any attempt to read
1142 from stdin should fail with an error, unless a sensible default has been
1142 from stdin should fail with an error, unless a sensible default has been
1143 specified.
1143 specified.
1144
1144
1145 Interactiveness is triggered by the value of the `ui.interactive'
1145 Interactiveness is triggered by the value of the `ui.interactive'
1146 configuration variable or - if it is unset - when `sys.stdin' points
1146 configuration variable or - if it is unset - when `sys.stdin' points
1147 to a terminal device.
1147 to a terminal device.
1148
1148
1149 This function refers to input only; for output, see `ui.formatted()'.
1149 This function refers to input only; for output, see `ui.formatted()'.
1150 '''
1150 '''
1151 i = self.configbool("ui", "interactive")
1151 i = self.configbool("ui", "interactive")
1152 if i is None:
1152 if i is None:
1153 # some environments replace stdin without implementing isatty
1153 # some environments replace stdin without implementing isatty
1154 # usually those are non-interactive
1154 # usually those are non-interactive
1155 return self._isatty(self.fin)
1155 return self._isatty(self.fin)
1156
1156
1157 return i
1157 return i
1158
1158
1159 def termwidth(self):
1159 def termwidth(self):
1160 '''how wide is the terminal in columns?
1160 '''how wide is the terminal in columns?
1161 '''
1161 '''
1162 if 'COLUMNS' in encoding.environ:
1162 if 'COLUMNS' in encoding.environ:
1163 try:
1163 try:
1164 return int(encoding.environ['COLUMNS'])
1164 return int(encoding.environ['COLUMNS'])
1165 except ValueError:
1165 except ValueError:
1166 pass
1166 pass
1167 return scmutil.termsize(self)[0]
1167 return scmutil.termsize(self)[0]
1168
1168
1169 def formatted(self):
1169 def formatted(self):
1170 '''should formatted output be used?
1170 '''should formatted output be used?
1171
1171
1172 It is often desirable to format the output to suite the output medium.
1172 It is often desirable to format the output to suite the output medium.
1173 Examples of this are truncating long lines or colorizing messages.
1173 Examples of this are truncating long lines or colorizing messages.
1174 However, this is not often not desirable when piping output into other
1174 However, this is not often not desirable when piping output into other
1175 utilities, e.g. `grep'.
1175 utilities, e.g. `grep'.
1176
1176
1177 Formatted output is triggered by the value of the `ui.formatted'
1177 Formatted output is triggered by the value of the `ui.formatted'
1178 configuration variable or - if it is unset - when `sys.stdout' points
1178 configuration variable or - if it is unset - when `sys.stdout' points
1179 to a terminal device. Please note that `ui.formatted' should be
1179 to a terminal device. Please note that `ui.formatted' should be
1180 considered an implementation detail; it is not intended for use outside
1180 considered an implementation detail; it is not intended for use outside
1181 Mercurial or its extensions.
1181 Mercurial or its extensions.
1182
1182
1183 This function refers to output only; for input, see `ui.interactive()'.
1183 This function refers to output only; for input, see `ui.interactive()'.
1184 This function always returns false when in plain mode, see `ui.plain()'.
1184 This function always returns false when in plain mode, see `ui.plain()'.
1185 '''
1185 '''
1186 if self.plain():
1186 if self.plain():
1187 return False
1187 return False
1188
1188
1189 i = self.configbool("ui", "formatted")
1189 i = self.configbool("ui", "formatted")
1190 if i is None:
1190 if i is None:
1191 # some environments replace stdout without implementing isatty
1191 # some environments replace stdout without implementing isatty
1192 # usually those are non-interactive
1192 # usually those are non-interactive
1193 return self._isatty(self.fout)
1193 return self._isatty(self.fout)
1194
1194
1195 return i
1195 return i
1196
1196
1197 def _readline(self, prompt=''):
1197 def _readline(self, prompt=''):
1198 if self._isatty(self.fin):
1198 if self._isatty(self.fin):
1199 try:
1199 try:
1200 # magically add command line editing support, where
1200 # magically add command line editing support, where
1201 # available
1201 # available
1202 import readline
1202 import readline
1203 # force demandimport to really load the module
1203 # force demandimport to really load the module
1204 readline.read_history_file
1204 readline.read_history_file
1205 # windows sometimes raises something other than ImportError
1205 # windows sometimes raises something other than ImportError
1206 except Exception:
1206 except Exception:
1207 pass
1207 pass
1208
1208
1209 # call write() so output goes through subclassed implementation
1209 # call write() so output goes through subclassed implementation
1210 # e.g. color extension on Windows
1210 # e.g. color extension on Windows
1211 self.write(prompt, prompt=True)
1211 self.write(prompt, prompt=True)
1212
1212
1213 # instead of trying to emulate raw_input, swap (self.fin,
1213 # instead of trying to emulate raw_input, swap (self.fin,
1214 # self.fout) with (sys.stdin, sys.stdout)
1214 # self.fout) with (sys.stdin, sys.stdout)
1215 oldin = sys.stdin
1215 oldin = sys.stdin
1216 oldout = sys.stdout
1216 oldout = sys.stdout
1217 sys.stdin = self.fin
1217 sys.stdin = self.fin
1218 sys.stdout = self.fout
1218 sys.stdout = self.fout
1219 # prompt ' ' must exist; otherwise readline may delete entire line
1219 # prompt ' ' must exist; otherwise readline may delete entire line
1220 # - http://bugs.python.org/issue12833
1220 # - http://bugs.python.org/issue12833
1221 with self.timeblockedsection('stdio'):
1221 with self.timeblockedsection('stdio'):
1222 line = raw_input(' ')
1222 line = raw_input(' ')
1223 sys.stdin = oldin
1223 sys.stdin = oldin
1224 sys.stdout = oldout
1224 sys.stdout = oldout
1225
1225
1226 # When stdin is in binary mode on Windows, it can cause
1226 # When stdin is in binary mode on Windows, it can cause
1227 # raw_input() to emit an extra trailing carriage return
1227 # raw_input() to emit an extra trailing carriage return
1228 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1228 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1229 line = line[:-1]
1229 line = line[:-1]
1230 return line
1230 return line
1231
1231
1232 def prompt(self, msg, default="y"):
1232 def prompt(self, msg, default="y"):
1233 """Prompt user with msg, read response.
1233 """Prompt user with msg, read response.
1234 If ui is not interactive, the default is returned.
1234 If ui is not interactive, the default is returned.
1235 """
1235 """
1236 if not self.interactive():
1236 if not self.interactive():
1237 self.write(msg, ' ', default or '', "\n")
1237 self.write(msg, ' ', default or '', "\n")
1238 return default
1238 return default
1239 try:
1239 try:
1240 r = self._readline(self.label(msg, 'ui.prompt'))
1240 r = self._readline(self.label(msg, 'ui.prompt'))
1241 if not r:
1241 if not r:
1242 r = default
1242 r = default
1243 if self.configbool('ui', 'promptecho'):
1243 if self.configbool('ui', 'promptecho'):
1244 self.write(r, "\n")
1244 self.write(r, "\n")
1245 return r
1245 return r
1246 except EOFError:
1246 except EOFError:
1247 raise error.ResponseExpected()
1247 raise error.ResponseExpected()
1248
1248
1249 @staticmethod
1249 @staticmethod
1250 def extractchoices(prompt):
1250 def extractchoices(prompt):
1251 """Extract prompt message and list of choices from specified prompt.
1251 """Extract prompt message and list of choices from specified prompt.
1252
1252
1253 This returns tuple "(message, choices)", and "choices" is the
1253 This returns tuple "(message, choices)", and "choices" is the
1254 list of tuple "(response character, text without &)".
1254 list of tuple "(response character, text without &)".
1255
1255
1256 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1256 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1257 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1257 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1258 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1258 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1259 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1259 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1260 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1260 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1261 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1261 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1262 """
1262 """
1263
1263
1264 # Sadly, the prompt string may have been built with a filename
1264 # Sadly, the prompt string may have been built with a filename
1265 # containing "$$" so let's try to find the first valid-looking
1265 # containing "$$" so let's try to find the first valid-looking
1266 # prompt to start parsing. Sadly, we also can't rely on
1266 # prompt to start parsing. Sadly, we also can't rely on
1267 # choices containing spaces, ASCII, or basically anything
1267 # choices containing spaces, ASCII, or basically anything
1268 # except an ampersand followed by a character.
1268 # except an ampersand followed by a character.
1269 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1269 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1270 msg = m.group(1)
1270 msg = m.group(1)
1271 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1271 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1272 return (msg,
1272 def choicetuple(s):
1273 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1273 ampidx = s.index('&')
1274 for s in choices])
1274 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1275 return (msg, [choicetuple(s) for s in choices])
1275
1276
1276 def promptchoice(self, prompt, default=0):
1277 def promptchoice(self, prompt, default=0):
1277 """Prompt user with a message, read response, and ensure it matches
1278 """Prompt user with a message, read response, and ensure it matches
1278 one of the provided choices. The prompt is formatted as follows:
1279 one of the provided choices. The prompt is formatted as follows:
1279
1280
1280 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1281 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1281
1282
1282 The index of the choice is returned. Responses are case
1283 The index of the choice is returned. Responses are case
1283 insensitive. If ui is not interactive, the default is
1284 insensitive. If ui is not interactive, the default is
1284 returned.
1285 returned.
1285 """
1286 """
1286
1287
1287 msg, choices = self.extractchoices(prompt)
1288 msg, choices = self.extractchoices(prompt)
1288 resps = [r for r, t in choices]
1289 resps = [r for r, t in choices]
1289 while True:
1290 while True:
1290 r = self.prompt(msg, resps[default])
1291 r = self.prompt(msg, resps[default])
1291 if r.lower() in resps:
1292 if r.lower() in resps:
1292 return resps.index(r.lower())
1293 return resps.index(r.lower())
1293 self.write(_("unrecognized response\n"))
1294 self.write(_("unrecognized response\n"))
1294
1295
1295 def getpass(self, prompt=None, default=None):
1296 def getpass(self, prompt=None, default=None):
1296 if not self.interactive():
1297 if not self.interactive():
1297 return default
1298 return default
1298 try:
1299 try:
1299 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1300 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1300 # disable getpass() only if explicitly specified. it's still valid
1301 # disable getpass() only if explicitly specified. it's still valid
1301 # to interact with tty even if fin is not a tty.
1302 # to interact with tty even if fin is not a tty.
1302 with self.timeblockedsection('stdio'):
1303 with self.timeblockedsection('stdio'):
1303 if self.configbool('ui', 'nontty'):
1304 if self.configbool('ui', 'nontty'):
1304 l = self.fin.readline()
1305 l = self.fin.readline()
1305 if not l:
1306 if not l:
1306 raise EOFError
1307 raise EOFError
1307 return l.rstrip('\n')
1308 return l.rstrip('\n')
1308 else:
1309 else:
1309 return getpass.getpass('')
1310 return getpass.getpass('')
1310 except EOFError:
1311 except EOFError:
1311 raise error.ResponseExpected()
1312 raise error.ResponseExpected()
1312 def status(self, *msg, **opts):
1313 def status(self, *msg, **opts):
1313 '''write status message to output (if ui.quiet is False)
1314 '''write status message to output (if ui.quiet is False)
1314
1315
1315 This adds an output label of "ui.status".
1316 This adds an output label of "ui.status".
1316 '''
1317 '''
1317 if not self.quiet:
1318 if not self.quiet:
1318 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1319 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1319 self.write(*msg, **opts)
1320 self.write(*msg, **opts)
1320 def warn(self, *msg, **opts):
1321 def warn(self, *msg, **opts):
1321 '''write warning message to output (stderr)
1322 '''write warning message to output (stderr)
1322
1323
1323 This adds an output label of "ui.warning".
1324 This adds an output label of "ui.warning".
1324 '''
1325 '''
1325 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1326 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1326 self.write_err(*msg, **opts)
1327 self.write_err(*msg, **opts)
1327 def note(self, *msg, **opts):
1328 def note(self, *msg, **opts):
1328 '''write note to output (if ui.verbose is True)
1329 '''write note to output (if ui.verbose is True)
1329
1330
1330 This adds an output label of "ui.note".
1331 This adds an output label of "ui.note".
1331 '''
1332 '''
1332 if self.verbose:
1333 if self.verbose:
1333 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1334 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1334 self.write(*msg, **opts)
1335 self.write(*msg, **opts)
1335 def debug(self, *msg, **opts):
1336 def debug(self, *msg, **opts):
1336 '''write debug message to output (if ui.debugflag is True)
1337 '''write debug message to output (if ui.debugflag is True)
1337
1338
1338 This adds an output label of "ui.debug".
1339 This adds an output label of "ui.debug".
1339 '''
1340 '''
1340 if self.debugflag:
1341 if self.debugflag:
1341 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1342 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1342 self.write(*msg, **opts)
1343 self.write(*msg, **opts)
1343
1344
1344 def edit(self, text, user, extra=None, editform=None, pending=None,
1345 def edit(self, text, user, extra=None, editform=None, pending=None,
1345 repopath=None):
1346 repopath=None):
1346 extra_defaults = {
1347 extra_defaults = {
1347 'prefix': 'editor',
1348 'prefix': 'editor',
1348 'suffix': '.txt',
1349 'suffix': '.txt',
1349 }
1350 }
1350 if extra is not None:
1351 if extra is not None:
1351 extra_defaults.update(extra)
1352 extra_defaults.update(extra)
1352 extra = extra_defaults
1353 extra = extra_defaults
1353
1354
1354 rdir = None
1355 rdir = None
1355 if self.configbool('experimental', 'editortmpinhg'):
1356 if self.configbool('experimental', 'editortmpinhg'):
1356 rdir = repopath
1357 rdir = repopath
1357 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1358 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1358 suffix=extra['suffix'],
1359 suffix=extra['suffix'],
1359 dir=rdir)
1360 dir=rdir)
1360 try:
1361 try:
1361 f = os.fdopen(fd, r'wb')
1362 f = os.fdopen(fd, r'wb')
1362 f.write(util.tonativeeol(text))
1363 f.write(util.tonativeeol(text))
1363 f.close()
1364 f.close()
1364
1365
1365 environ = {'HGUSER': user}
1366 environ = {'HGUSER': user}
1366 if 'transplant_source' in extra:
1367 if 'transplant_source' in extra:
1367 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1368 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1368 for label in ('intermediate-source', 'source', 'rebase_source'):
1369 for label in ('intermediate-source', 'source', 'rebase_source'):
1369 if label in extra:
1370 if label in extra:
1370 environ.update({'HGREVISION': extra[label]})
1371 environ.update({'HGREVISION': extra[label]})
1371 break
1372 break
1372 if editform:
1373 if editform:
1373 environ.update({'HGEDITFORM': editform})
1374 environ.update({'HGEDITFORM': editform})
1374 if pending:
1375 if pending:
1375 environ.update({'HG_PENDING': pending})
1376 environ.update({'HG_PENDING': pending})
1376
1377
1377 editor = self.geteditor()
1378 editor = self.geteditor()
1378
1379
1379 self.system("%s \"%s\"" % (editor, name),
1380 self.system("%s \"%s\"" % (editor, name),
1380 environ=environ,
1381 environ=environ,
1381 onerr=error.Abort, errprefix=_("edit failed"),
1382 onerr=error.Abort, errprefix=_("edit failed"),
1382 blockedtag='editor')
1383 blockedtag='editor')
1383
1384
1384 f = open(name, r'rb')
1385 f = open(name, r'rb')
1385 t = util.fromnativeeol(f.read())
1386 t = util.fromnativeeol(f.read())
1386 f.close()
1387 f.close()
1387 finally:
1388 finally:
1388 os.unlink(name)
1389 os.unlink(name)
1389
1390
1390 return t
1391 return t
1391
1392
1392 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1393 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1393 blockedtag=None):
1394 blockedtag=None):
1394 '''execute shell command with appropriate output stream. command
1395 '''execute shell command with appropriate output stream. command
1395 output will be redirected if fout is not stdout.
1396 output will be redirected if fout is not stdout.
1396
1397
1397 if command fails and onerr is None, return status, else raise onerr
1398 if command fails and onerr is None, return status, else raise onerr
1398 object as exception.
1399 object as exception.
1399 '''
1400 '''
1400 if blockedtag is None:
1401 if blockedtag is None:
1401 # Long cmds tend to be because of an absolute path on cmd. Keep
1402 # Long cmds tend to be because of an absolute path on cmd. Keep
1402 # the tail end instead
1403 # the tail end instead
1403 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1404 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1404 blockedtag = 'unknown_system_' + cmdsuffix
1405 blockedtag = 'unknown_system_' + cmdsuffix
1405 out = self.fout
1406 out = self.fout
1406 if any(s[1] for s in self._bufferstates):
1407 if any(s[1] for s in self._bufferstates):
1407 out = self
1408 out = self
1408 with self.timeblockedsection(blockedtag):
1409 with self.timeblockedsection(blockedtag):
1409 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1410 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1410 if rc and onerr:
1411 if rc and onerr:
1411 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1412 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1412 util.explainexit(rc)[0])
1413 util.explainexit(rc)[0])
1413 if errprefix:
1414 if errprefix:
1414 errmsg = '%s: %s' % (errprefix, errmsg)
1415 errmsg = '%s: %s' % (errprefix, errmsg)
1415 raise onerr(errmsg)
1416 raise onerr(errmsg)
1416 return rc
1417 return rc
1417
1418
1418 def _runsystem(self, cmd, environ, cwd, out):
1419 def _runsystem(self, cmd, environ, cwd, out):
1419 """actually execute the given shell command (can be overridden by
1420 """actually execute the given shell command (can be overridden by
1420 extensions like chg)"""
1421 extensions like chg)"""
1421 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1422 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1422
1423
1423 def traceback(self, exc=None, force=False):
1424 def traceback(self, exc=None, force=False):
1424 '''print exception traceback if traceback printing enabled or forced.
1425 '''print exception traceback if traceback printing enabled or forced.
1425 only to call in exception handler. returns true if traceback
1426 only to call in exception handler. returns true if traceback
1426 printed.'''
1427 printed.'''
1427 if self.tracebackflag or force:
1428 if self.tracebackflag or force:
1428 if exc is None:
1429 if exc is None:
1429 exc = sys.exc_info()
1430 exc = sys.exc_info()
1430 cause = getattr(exc[1], 'cause', None)
1431 cause = getattr(exc[1], 'cause', None)
1431
1432
1432 if cause is not None:
1433 if cause is not None:
1433 causetb = traceback.format_tb(cause[2])
1434 causetb = traceback.format_tb(cause[2])
1434 exctb = traceback.format_tb(exc[2])
1435 exctb = traceback.format_tb(exc[2])
1435 exconly = traceback.format_exception_only(cause[0], cause[1])
1436 exconly = traceback.format_exception_only(cause[0], cause[1])
1436
1437
1437 # exclude frame where 'exc' was chained and rethrown from exctb
1438 # exclude frame where 'exc' was chained and rethrown from exctb
1438 self.write_err('Traceback (most recent call last):\n',
1439 self.write_err('Traceback (most recent call last):\n',
1439 ''.join(exctb[:-1]),
1440 ''.join(exctb[:-1]),
1440 ''.join(causetb),
1441 ''.join(causetb),
1441 ''.join(exconly))
1442 ''.join(exconly))
1442 else:
1443 else:
1443 output = traceback.format_exception(exc[0], exc[1], exc[2])
1444 output = traceback.format_exception(exc[0], exc[1], exc[2])
1444 data = r''.join(output)
1445 data = r''.join(output)
1445 if pycompat.ispy3:
1446 if pycompat.ispy3:
1446 enc = pycompat.sysstr(encoding.encoding)
1447 enc = pycompat.sysstr(encoding.encoding)
1447 data = data.encode(enc, errors=r'replace')
1448 data = data.encode(enc, errors=r'replace')
1448 self.write_err(data)
1449 self.write_err(data)
1449 return self.tracebackflag or force
1450 return self.tracebackflag or force
1450
1451
1451 def geteditor(self):
1452 def geteditor(self):
1452 '''return editor to use'''
1453 '''return editor to use'''
1453 if pycompat.sysplatform == 'plan9':
1454 if pycompat.sysplatform == 'plan9':
1454 # vi is the MIPS instruction simulator on Plan 9. We
1455 # vi is the MIPS instruction simulator on Plan 9. We
1455 # instead default to E to plumb commit messages to
1456 # instead default to E to plumb commit messages to
1456 # avoid confusion.
1457 # avoid confusion.
1457 editor = 'E'
1458 editor = 'E'
1458 else:
1459 else:
1459 editor = 'vi'
1460 editor = 'vi'
1460 return (encoding.environ.get("HGEDITOR") or
1461 return (encoding.environ.get("HGEDITOR") or
1461 self.config("ui", "editor", editor))
1462 self.config("ui", "editor", editor))
1462
1463
1463 @util.propertycache
1464 @util.propertycache
1464 def _progbar(self):
1465 def _progbar(self):
1465 """setup the progbar singleton to the ui object"""
1466 """setup the progbar singleton to the ui object"""
1466 if (self.quiet or self.debugflag
1467 if (self.quiet or self.debugflag
1467 or self.configbool('progress', 'disable')
1468 or self.configbool('progress', 'disable')
1468 or not progress.shouldprint(self)):
1469 or not progress.shouldprint(self)):
1469 return None
1470 return None
1470 return getprogbar(self)
1471 return getprogbar(self)
1471
1472
1472 def _progclear(self):
1473 def _progclear(self):
1473 """clear progress bar output if any. use it before any output"""
1474 """clear progress bar output if any. use it before any output"""
1474 if '_progbar' not in vars(self): # nothing loaded yet
1475 if '_progbar' not in vars(self): # nothing loaded yet
1475 return
1476 return
1476 if self._progbar is not None and self._progbar.printed:
1477 if self._progbar is not None and self._progbar.printed:
1477 self._progbar.clear()
1478 self._progbar.clear()
1478
1479
1479 def progress(self, topic, pos, item="", unit="", total=None):
1480 def progress(self, topic, pos, item="", unit="", total=None):
1480 '''show a progress message
1481 '''show a progress message
1481
1482
1482 By default a textual progress bar will be displayed if an operation
1483 By default a textual progress bar will be displayed if an operation
1483 takes too long. 'topic' is the current operation, 'item' is a
1484 takes too long. 'topic' is the current operation, 'item' is a
1484 non-numeric marker of the current position (i.e. the currently
1485 non-numeric marker of the current position (i.e. the currently
1485 in-process file), 'pos' is the current numeric position (i.e.
1486 in-process file), 'pos' is the current numeric position (i.e.
1486 revision, bytes, etc.), unit is a corresponding unit label,
1487 revision, bytes, etc.), unit is a corresponding unit label,
1487 and total is the highest expected pos.
1488 and total is the highest expected pos.
1488
1489
1489 Multiple nested topics may be active at a time.
1490 Multiple nested topics may be active at a time.
1490
1491
1491 All topics should be marked closed by setting pos to None at
1492 All topics should be marked closed by setting pos to None at
1492 termination.
1493 termination.
1493 '''
1494 '''
1494 if self._progbar is not None:
1495 if self._progbar is not None:
1495 self._progbar.progress(topic, pos, item=item, unit=unit,
1496 self._progbar.progress(topic, pos, item=item, unit=unit,
1496 total=total)
1497 total=total)
1497 if pos is None or not self.configbool('progress', 'debug'):
1498 if pos is None or not self.configbool('progress', 'debug'):
1498 return
1499 return
1499
1500
1500 if unit:
1501 if unit:
1501 unit = ' ' + unit
1502 unit = ' ' + unit
1502 if item:
1503 if item:
1503 item = ' ' + item
1504 item = ' ' + item
1504
1505
1505 if total:
1506 if total:
1506 pct = 100.0 * pos / total
1507 pct = 100.0 * pos / total
1507 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1508 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1508 % (topic, item, pos, total, unit, pct))
1509 % (topic, item, pos, total, unit, pct))
1509 else:
1510 else:
1510 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1511 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1511
1512
1512 def log(self, service, *msg, **opts):
1513 def log(self, service, *msg, **opts):
1513 '''hook for logging facility extensions
1514 '''hook for logging facility extensions
1514
1515
1515 service should be a readily-identifiable subsystem, which will
1516 service should be a readily-identifiable subsystem, which will
1516 allow filtering.
1517 allow filtering.
1517
1518
1518 *msg should be a newline-terminated format string to log, and
1519 *msg should be a newline-terminated format string to log, and
1519 then any values to %-format into that format string.
1520 then any values to %-format into that format string.
1520
1521
1521 **opts currently has no defined meanings.
1522 **opts currently has no defined meanings.
1522 '''
1523 '''
1523
1524
1524 def label(self, msg, label):
1525 def label(self, msg, label):
1525 '''style msg based on supplied label
1526 '''style msg based on supplied label
1526
1527
1527 If some color mode is enabled, this will add the necessary control
1528 If some color mode is enabled, this will add the necessary control
1528 characters to apply such color. In addition, 'debug' color mode adds
1529 characters to apply such color. In addition, 'debug' color mode adds
1529 markup showing which label affects a piece of text.
1530 markup showing which label affects a piece of text.
1530
1531
1531 ui.write(s, 'label') is equivalent to
1532 ui.write(s, 'label') is equivalent to
1532 ui.write(ui.label(s, 'label')).
1533 ui.write(ui.label(s, 'label')).
1533 '''
1534 '''
1534 if self._colormode is not None:
1535 if self._colormode is not None:
1535 return color.colorlabel(self, msg, label)
1536 return color.colorlabel(self, msg, label)
1536 return msg
1537 return msg
1537
1538
1538 def develwarn(self, msg, stacklevel=1, config=None):
1539 def develwarn(self, msg, stacklevel=1, config=None):
1539 """issue a developer warning message
1540 """issue a developer warning message
1540
1541
1541 Use 'stacklevel' to report the offender some layers further up in the
1542 Use 'stacklevel' to report the offender some layers further up in the
1542 stack.
1543 stack.
1543 """
1544 """
1544 if not self.configbool('devel', 'all-warnings'):
1545 if not self.configbool('devel', 'all-warnings'):
1545 if config is not None and not self.configbool('devel', config):
1546 if config is not None and not self.configbool('devel', config):
1546 return
1547 return
1547 msg = 'devel-warn: ' + msg
1548 msg = 'devel-warn: ' + msg
1548 stacklevel += 1 # get in develwarn
1549 stacklevel += 1 # get in develwarn
1549 if self.tracebackflag:
1550 if self.tracebackflag:
1550 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1551 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1551 self.log('develwarn', '%s at:\n%s' %
1552 self.log('develwarn', '%s at:\n%s' %
1552 (msg, ''.join(util.getstackframes(stacklevel))))
1553 (msg, ''.join(util.getstackframes(stacklevel))))
1553 else:
1554 else:
1554 curframe = inspect.currentframe()
1555 curframe = inspect.currentframe()
1555 calframe = inspect.getouterframes(curframe, 2)
1556 calframe = inspect.getouterframes(curframe, 2)
1556 self.write_err('%s at: %s:%s (%s)\n'
1557 self.write_err('%s at: %s:%s (%s)\n'
1557 % ((msg,) + calframe[stacklevel][1:4]))
1558 % ((msg,) + calframe[stacklevel][1:4]))
1558 self.log('develwarn', '%s at: %s:%s (%s)\n',
1559 self.log('develwarn', '%s at: %s:%s (%s)\n',
1559 msg, *calframe[stacklevel][1:4])
1560 msg, *calframe[stacklevel][1:4])
1560 curframe = calframe = None # avoid cycles
1561 curframe = calframe = None # avoid cycles
1561
1562
1562 def deprecwarn(self, msg, version):
1563 def deprecwarn(self, msg, version):
1563 """issue a deprecation warning
1564 """issue a deprecation warning
1564
1565
1565 - msg: message explaining what is deprecated and how to upgrade,
1566 - msg: message explaining what is deprecated and how to upgrade,
1566 - version: last version where the API will be supported,
1567 - version: last version where the API will be supported,
1567 """
1568 """
1568 if not (self.configbool('devel', 'all-warnings')
1569 if not (self.configbool('devel', 'all-warnings')
1569 or self.configbool('devel', 'deprec-warn')):
1570 or self.configbool('devel', 'deprec-warn')):
1570 return
1571 return
1571 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1572 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1572 " update your code.)") % version
1573 " update your code.)") % version
1573 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1574 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1574
1575
1575 def exportableenviron(self):
1576 def exportableenviron(self):
1576 """The environment variables that are safe to export, e.g. through
1577 """The environment variables that are safe to export, e.g. through
1577 hgweb.
1578 hgweb.
1578 """
1579 """
1579 return self._exportableenviron
1580 return self._exportableenviron
1580
1581
1581 @contextlib.contextmanager
1582 @contextlib.contextmanager
1582 def configoverride(self, overrides, source=""):
1583 def configoverride(self, overrides, source=""):
1583 """Context manager for temporary config overrides
1584 """Context manager for temporary config overrides
1584 `overrides` must be a dict of the following structure:
1585 `overrides` must be a dict of the following structure:
1585 {(section, name) : value}"""
1586 {(section, name) : value}"""
1586 backups = {}
1587 backups = {}
1587 try:
1588 try:
1588 for (section, name), value in overrides.items():
1589 for (section, name), value in overrides.items():
1589 backups[(section, name)] = self.backupconfig(section, name)
1590 backups[(section, name)] = self.backupconfig(section, name)
1590 self.setconfig(section, name, value, source)
1591 self.setconfig(section, name, value, source)
1591 yield
1592 yield
1592 finally:
1593 finally:
1593 for __, backup in backups.items():
1594 for __, backup in backups.items():
1594 self.restoreconfig(backup)
1595 self.restoreconfig(backup)
1595 # just restoring ui.quiet config to the previous value is not enough
1596 # just restoring ui.quiet config to the previous value is not enough
1596 # as it does not update ui.quiet class member
1597 # as it does not update ui.quiet class member
1597 if ('ui', 'quiet') in overrides:
1598 if ('ui', 'quiet') in overrides:
1598 self.fixconfig(section='ui')
1599 self.fixconfig(section='ui')
1599
1600
1600 class paths(dict):
1601 class paths(dict):
1601 """Represents a collection of paths and their configs.
1602 """Represents a collection of paths and their configs.
1602
1603
1603 Data is initially derived from ui instances and the config files they have
1604 Data is initially derived from ui instances and the config files they have
1604 loaded.
1605 loaded.
1605 """
1606 """
1606 def __init__(self, ui):
1607 def __init__(self, ui):
1607 dict.__init__(self)
1608 dict.__init__(self)
1608
1609
1609 for name, loc in ui.configitems('paths', ignoresub=True):
1610 for name, loc in ui.configitems('paths', ignoresub=True):
1610 # No location is the same as not existing.
1611 # No location is the same as not existing.
1611 if not loc:
1612 if not loc:
1612 continue
1613 continue
1613 loc, sub = ui.configsuboptions('paths', name)
1614 loc, sub = ui.configsuboptions('paths', name)
1614 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1615 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1615
1616
1616 def getpath(self, name, default=None):
1617 def getpath(self, name, default=None):
1617 """Return a ``path`` from a string, falling back to default.
1618 """Return a ``path`` from a string, falling back to default.
1618
1619
1619 ``name`` can be a named path or locations. Locations are filesystem
1620 ``name`` can be a named path or locations. Locations are filesystem
1620 paths or URIs.
1621 paths or URIs.
1621
1622
1622 Returns None if ``name`` is not a registered path, a URI, or a local
1623 Returns None if ``name`` is not a registered path, a URI, or a local
1623 path to a repo.
1624 path to a repo.
1624 """
1625 """
1625 # Only fall back to default if no path was requested.
1626 # Only fall back to default if no path was requested.
1626 if name is None:
1627 if name is None:
1627 if not default:
1628 if not default:
1628 default = ()
1629 default = ()
1629 elif not isinstance(default, (tuple, list)):
1630 elif not isinstance(default, (tuple, list)):
1630 default = (default,)
1631 default = (default,)
1631 for k in default:
1632 for k in default:
1632 try:
1633 try:
1633 return self[k]
1634 return self[k]
1634 except KeyError:
1635 except KeyError:
1635 continue
1636 continue
1636 return None
1637 return None
1637
1638
1638 # Most likely empty string.
1639 # Most likely empty string.
1639 # This may need to raise in the future.
1640 # This may need to raise in the future.
1640 if not name:
1641 if not name:
1641 return None
1642 return None
1642
1643
1643 try:
1644 try:
1644 return self[name]
1645 return self[name]
1645 except KeyError:
1646 except KeyError:
1646 # Try to resolve as a local path or URI.
1647 # Try to resolve as a local path or URI.
1647 try:
1648 try:
1648 # We don't pass sub-options in, so no need to pass ui instance.
1649 # We don't pass sub-options in, so no need to pass ui instance.
1649 return path(None, None, rawloc=name)
1650 return path(None, None, rawloc=name)
1650 except ValueError:
1651 except ValueError:
1651 raise error.RepoError(_('repository %s does not exist') %
1652 raise error.RepoError(_('repository %s does not exist') %
1652 name)
1653 name)
1653
1654
1654 _pathsuboptions = {}
1655 _pathsuboptions = {}
1655
1656
1656 def pathsuboption(option, attr):
1657 def pathsuboption(option, attr):
1657 """Decorator used to declare a path sub-option.
1658 """Decorator used to declare a path sub-option.
1658
1659
1659 Arguments are the sub-option name and the attribute it should set on
1660 Arguments are the sub-option name and the attribute it should set on
1660 ``path`` instances.
1661 ``path`` instances.
1661
1662
1662 The decorated function will receive as arguments a ``ui`` instance,
1663 The decorated function will receive as arguments a ``ui`` instance,
1663 ``path`` instance, and the string value of this option from the config.
1664 ``path`` instance, and the string value of this option from the config.
1664 The function should return the value that will be set on the ``path``
1665 The function should return the value that will be set on the ``path``
1665 instance.
1666 instance.
1666
1667
1667 This decorator can be used to perform additional verification of
1668 This decorator can be used to perform additional verification of
1668 sub-options and to change the type of sub-options.
1669 sub-options and to change the type of sub-options.
1669 """
1670 """
1670 def register(func):
1671 def register(func):
1671 _pathsuboptions[option] = (attr, func)
1672 _pathsuboptions[option] = (attr, func)
1672 return func
1673 return func
1673 return register
1674 return register
1674
1675
1675 @pathsuboption('pushurl', 'pushloc')
1676 @pathsuboption('pushurl', 'pushloc')
1676 def pushurlpathoption(ui, path, value):
1677 def pushurlpathoption(ui, path, value):
1677 u = util.url(value)
1678 u = util.url(value)
1678 # Actually require a URL.
1679 # Actually require a URL.
1679 if not u.scheme:
1680 if not u.scheme:
1680 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1681 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1681 return None
1682 return None
1682
1683
1683 # Don't support the #foo syntax in the push URL to declare branch to
1684 # Don't support the #foo syntax in the push URL to declare branch to
1684 # push.
1685 # push.
1685 if u.fragment:
1686 if u.fragment:
1686 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1687 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1687 'ignoring)\n') % path.name)
1688 'ignoring)\n') % path.name)
1688 u.fragment = None
1689 u.fragment = None
1689
1690
1690 return str(u)
1691 return str(u)
1691
1692
1692 @pathsuboption('pushrev', 'pushrev')
1693 @pathsuboption('pushrev', 'pushrev')
1693 def pushrevpathoption(ui, path, value):
1694 def pushrevpathoption(ui, path, value):
1694 return value
1695 return value
1695
1696
1696 class path(object):
1697 class path(object):
1697 """Represents an individual path and its configuration."""
1698 """Represents an individual path and its configuration."""
1698
1699
1699 def __init__(self, ui, name, rawloc=None, suboptions=None):
1700 def __init__(self, ui, name, rawloc=None, suboptions=None):
1700 """Construct a path from its config options.
1701 """Construct a path from its config options.
1701
1702
1702 ``ui`` is the ``ui`` instance the path is coming from.
1703 ``ui`` is the ``ui`` instance the path is coming from.
1703 ``name`` is the symbolic name of the path.
1704 ``name`` is the symbolic name of the path.
1704 ``rawloc`` is the raw location, as defined in the config.
1705 ``rawloc`` is the raw location, as defined in the config.
1705 ``pushloc`` is the raw locations pushes should be made to.
1706 ``pushloc`` is the raw locations pushes should be made to.
1706
1707
1707 If ``name`` is not defined, we require that the location be a) a local
1708 If ``name`` is not defined, we require that the location be a) a local
1708 filesystem path with a .hg directory or b) a URL. If not,
1709 filesystem path with a .hg directory or b) a URL. If not,
1709 ``ValueError`` is raised.
1710 ``ValueError`` is raised.
1710 """
1711 """
1711 if not rawloc:
1712 if not rawloc:
1712 raise ValueError('rawloc must be defined')
1713 raise ValueError('rawloc must be defined')
1713
1714
1714 # Locations may define branches via syntax <base>#<branch>.
1715 # Locations may define branches via syntax <base>#<branch>.
1715 u = util.url(rawloc)
1716 u = util.url(rawloc)
1716 branch = None
1717 branch = None
1717 if u.fragment:
1718 if u.fragment:
1718 branch = u.fragment
1719 branch = u.fragment
1719 u.fragment = None
1720 u.fragment = None
1720
1721
1721 self.url = u
1722 self.url = u
1722 self.branch = branch
1723 self.branch = branch
1723
1724
1724 self.name = name
1725 self.name = name
1725 self.rawloc = rawloc
1726 self.rawloc = rawloc
1726 self.loc = '%s' % u
1727 self.loc = '%s' % u
1727
1728
1728 # When given a raw location but not a symbolic name, validate the
1729 # When given a raw location but not a symbolic name, validate the
1729 # location is valid.
1730 # location is valid.
1730 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1731 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1731 raise ValueError('location is not a URL or path to a local '
1732 raise ValueError('location is not a URL or path to a local '
1732 'repo: %s' % rawloc)
1733 'repo: %s' % rawloc)
1733
1734
1734 suboptions = suboptions or {}
1735 suboptions = suboptions or {}
1735
1736
1736 # Now process the sub-options. If a sub-option is registered, its
1737 # Now process the sub-options. If a sub-option is registered, its
1737 # attribute will always be present. The value will be None if there
1738 # attribute will always be present. The value will be None if there
1738 # was no valid sub-option.
1739 # was no valid sub-option.
1739 for suboption, (attr, func) in _pathsuboptions.iteritems():
1740 for suboption, (attr, func) in _pathsuboptions.iteritems():
1740 if suboption not in suboptions:
1741 if suboption not in suboptions:
1741 setattr(self, attr, None)
1742 setattr(self, attr, None)
1742 continue
1743 continue
1743
1744
1744 value = func(ui, self, suboptions[suboption])
1745 value = func(ui, self, suboptions[suboption])
1745 setattr(self, attr, value)
1746 setattr(self, attr, value)
1746
1747
1747 def _isvalidlocalpath(self, path):
1748 def _isvalidlocalpath(self, path):
1748 """Returns True if the given path is a potentially valid repository.
1749 """Returns True if the given path is a potentially valid repository.
1749 This is its own function so that extensions can change the definition of
1750 This is its own function so that extensions can change the definition of
1750 'valid' in this case (like when pulling from a git repo into a hg
1751 'valid' in this case (like when pulling from a git repo into a hg
1751 one)."""
1752 one)."""
1752 return os.path.isdir(os.path.join(path, '.hg'))
1753 return os.path.isdir(os.path.join(path, '.hg'))
1753
1754
1754 @property
1755 @property
1755 def suboptions(self):
1756 def suboptions(self):
1756 """Return sub-options and their values for this path.
1757 """Return sub-options and their values for this path.
1757
1758
1758 This is intended to be used for presentation purposes.
1759 This is intended to be used for presentation purposes.
1759 """
1760 """
1760 d = {}
1761 d = {}
1761 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1762 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1762 value = getattr(self, attr)
1763 value = getattr(self, attr)
1763 if value is not None:
1764 if value is not None:
1764 d[subopt] = value
1765 d[subopt] = value
1765 return d
1766 return d
1766
1767
1767 # we instantiate one globally shared progress bar to avoid
1768 # we instantiate one globally shared progress bar to avoid
1768 # competing progress bars when multiple UI objects get created
1769 # competing progress bars when multiple UI objects get created
1769 _progresssingleton = None
1770 _progresssingleton = None
1770
1771
1771 def getprogbar(ui):
1772 def getprogbar(ui):
1772 global _progresssingleton
1773 global _progresssingleton
1773 if _progresssingleton is None:
1774 if _progresssingleton is None:
1774 # passing 'ui' object to the singleton is fishy,
1775 # passing 'ui' object to the singleton is fishy,
1775 # this is how the extension used to work but feel free to rework it.
1776 # this is how the extension used to work but feel free to rework it.
1776 _progresssingleton = progress.progbar(ui)
1777 _progresssingleton = progress.progbar(ui)
1777 return _progresssingleton
1778 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now