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