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