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