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