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