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