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