##// END OF EJS Templates
ui: add an uninterruptable context manager that can block SIGINT...
Augie Fackler -
r38545:313a940d default
parent child Browse files
Show More
@@ -0,0 +1,83 b''
1 Dummy extension simulating unsafe long running command
2 $ cat > sleepext.py <<EOF
3 > import time
4 > import itertools
5 >
6 > from mercurial import registrar
7 > from mercurial.i18n import _
8 >
9 > cmdtable = {}
10 > command = registrar.command(cmdtable)
11 >
12 > @command(b'sleep', [], _(b'TIME'), norepo=True)
13 > def sleep(ui, sleeptime=b"1", **opts):
14 > with ui.uninterruptable():
15 > for _i in itertools.repeat(None, int(sleeptime)):
16 > time.sleep(1)
17 > ui.warn(b"end of unsafe operation\n")
18 > ui.warn(b"%s second(s) passed\n" % sleeptime)
19 > EOF
20
21 Kludge to emulate timeout(1) which is not generally available.
22 $ cat > timeout.py <<EOF
23 > from __future__ import print_function
24 > import argparse
25 > import signal
26 > import subprocess
27 > import sys
28 > import time
29 >
30 > ap = argparse.ArgumentParser()
31 > ap.add_argument('-s', nargs=1, default='SIGTERM')
32 > ap.add_argument('duration', nargs=1, type=int)
33 > ap.add_argument('argv', nargs='*')
34 > opts = ap.parse_args()
35 > try:
36 > sig = int(opts.s[0])
37 > except ValueError:
38 > sname = opts.s[0]
39 > if not sname.startswith('SIG'):
40 > sname = 'SIG' + sname
41 > sig = getattr(signal, sname)
42 > proc = subprocess.Popen(opts.argv)
43 > time.sleep(opts.duration[0])
44 > proc.poll()
45 > if proc.returncode is None:
46 > proc.send_signal(sig)
47 > proc.wait()
48 > sys.exit(124)
49 > EOF
50
51 Set up repository
52 $ hg init repo
53 $ cd repo
54 $ cat >> $HGRCPATH << EOF
55 > [extensions]
56 > sleepext = ../sleepext.py
57 > EOF
58
59 Test ctrl-c
60 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
61 interrupted!
62 [124]
63
64 $ cat >> $HGRCPATH << EOF
65 > [experimental]
66 > nointerrupt = yes
67 > EOF
68
69 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
70 interrupted!
71 [124]
72
73 $ cat >> $HGRCPATH << EOF
74 > [experimental]
75 > nointerrupt-interactiveonly = False
76 > EOF
77
78 $ python $TESTTMP/timeout.py -s INT 1 hg sleep 2
79 shutting down cleanly
80 press ^C again to terminate immediately (dangerous)
81 end of unsafe operation
82 interrupted!
83 [124]
@@ -1,1355 +1,1358 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 functools
10 import functools
11 import re
11 import re
12
12
13 from . import (
13 from . import (
14 encoding,
14 encoding,
15 error,
15 error,
16 )
16 )
17
17
18 def loadconfigtable(ui, extname, configtable):
18 def loadconfigtable(ui, extname, configtable):
19 """update config item known to the ui with the extension ones"""
19 """update config item known to the ui with the extension ones"""
20 for section, items in sorted(configtable.items()):
20 for section, items in sorted(configtable.items()):
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownkeys = set(knownitems)
22 knownkeys = set(knownitems)
23 newkeys = set(items)
23 newkeys = set(items)
24 for key in sorted(knownkeys & newkeys):
24 for key in sorted(knownkeys & newkeys):
25 msg = "extension '%s' overwrite config item '%s.%s'"
25 msg = "extension '%s' overwrite config item '%s.%s'"
26 msg %= (extname, section, key)
26 msg %= (extname, section, key)
27 ui.develwarn(msg, config='warn-config')
27 ui.develwarn(msg, config='warn-config')
28
28
29 knownitems.update(items)
29 knownitems.update(items)
30
30
31 class configitem(object):
31 class configitem(object):
32 """represent a known config item
32 """represent a known config item
33
33
34 :section: the official config section where to find this item,
34 :section: the official config section where to find this item,
35 :name: the official name within the section,
35 :name: the official name within the section,
36 :default: default value for this item,
36 :default: default value for this item,
37 :alias: optional list of tuples as alternatives,
37 :alias: optional list of tuples as alternatives,
38 :generic: this is a generic definition, match name using regular expression.
38 :generic: this is a generic definition, match name using regular expression.
39 """
39 """
40
40
41 def __init__(self, section, name, default=None, alias=(),
41 def __init__(self, section, name, default=None, alias=(),
42 generic=False, priority=0):
42 generic=False, priority=0):
43 self.section = section
43 self.section = section
44 self.name = name
44 self.name = name
45 self.default = default
45 self.default = default
46 self.alias = list(alias)
46 self.alias = list(alias)
47 self.generic = generic
47 self.generic = generic
48 self.priority = priority
48 self.priority = priority
49 self._re = None
49 self._re = None
50 if generic:
50 if generic:
51 self._re = re.compile(self.name)
51 self._re = re.compile(self.name)
52
52
53 class itemregister(dict):
53 class itemregister(dict):
54 """A specialized dictionary that can handle wild-card selection"""
54 """A specialized dictionary that can handle wild-card selection"""
55
55
56 def __init__(self):
56 def __init__(self):
57 super(itemregister, self).__init__()
57 super(itemregister, self).__init__()
58 self._generics = set()
58 self._generics = set()
59
59
60 def update(self, other):
60 def update(self, other):
61 super(itemregister, self).update(other)
61 super(itemregister, self).update(other)
62 self._generics.update(other._generics)
62 self._generics.update(other._generics)
63
63
64 def __setitem__(self, key, item):
64 def __setitem__(self, key, item):
65 super(itemregister, self).__setitem__(key, item)
65 super(itemregister, self).__setitem__(key, item)
66 if item.generic:
66 if item.generic:
67 self._generics.add(item)
67 self._generics.add(item)
68
68
69 def get(self, key):
69 def get(self, key):
70 baseitem = super(itemregister, self).get(key)
70 baseitem = super(itemregister, self).get(key)
71 if baseitem is not None and not baseitem.generic:
71 if baseitem is not None and not baseitem.generic:
72 return baseitem
72 return baseitem
73
73
74 # search for a matching generic item
74 # search for a matching generic item
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 for item in generics:
76 for item in generics:
77 # we use 'match' instead of 'search' to make the matching simpler
77 # we use 'match' instead of 'search' to make the matching simpler
78 # for people unfamiliar with regular expression. Having the match
78 # for people unfamiliar with regular expression. Having the match
79 # rooted to the start of the string will produce less surprising
79 # rooted to the start of the string will produce less surprising
80 # result for user writing simple regex for sub-attribute.
80 # result for user writing simple regex for sub-attribute.
81 #
81 #
82 # For example using "color\..*" match produces an unsurprising
82 # For example using "color\..*" match produces an unsurprising
83 # result, while using search could suddenly match apparently
83 # result, while using search could suddenly match apparently
84 # unrelated configuration that happens to contains "color."
84 # unrelated configuration that happens to contains "color."
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 # some match to avoid the need to prefix most pattern with "^".
86 # some match to avoid the need to prefix most pattern with "^".
87 # The "^" seems more error prone.
87 # The "^" seems more error prone.
88 if item._re.match(key):
88 if item._re.match(key):
89 return item
89 return item
90
90
91 return None
91 return None
92
92
93 coreitems = {}
93 coreitems = {}
94
94
95 def _register(configtable, *args, **kwargs):
95 def _register(configtable, *args, **kwargs):
96 item = configitem(*args, **kwargs)
96 item = configitem(*args, **kwargs)
97 section = configtable.setdefault(item.section, itemregister())
97 section = configtable.setdefault(item.section, itemregister())
98 if item.name in section:
98 if item.name in section:
99 msg = "duplicated config item registration for '%s.%s'"
99 msg = "duplicated config item registration for '%s.%s'"
100 raise error.ProgrammingError(msg % (item.section, item.name))
100 raise error.ProgrammingError(msg % (item.section, item.name))
101 section[item.name] = item
101 section[item.name] = item
102
102
103 # special value for case where the default is derived from other values
103 # special value for case where the default is derived from other values
104 dynamicdefault = object()
104 dynamicdefault = object()
105
105
106 # Registering actual config items
106 # Registering actual config items
107
107
108 def getitemregister(configtable):
108 def getitemregister(configtable):
109 f = functools.partial(_register, configtable)
109 f = functools.partial(_register, configtable)
110 # export pseudo enum as configitem.*
110 # export pseudo enum as configitem.*
111 f.dynamicdefault = dynamicdefault
111 f.dynamicdefault = dynamicdefault
112 return f
112 return f
113
113
114 coreconfigitem = getitemregister(coreitems)
114 coreconfigitem = getitemregister(coreitems)
115
115
116 coreconfigitem('alias', '.*',
116 coreconfigitem('alias', '.*',
117 default=dynamicdefault,
117 default=dynamicdefault,
118 generic=True,
118 generic=True,
119 )
119 )
120 coreconfigitem('annotate', 'nodates',
120 coreconfigitem('annotate', 'nodates',
121 default=False,
121 default=False,
122 )
122 )
123 coreconfigitem('annotate', 'showfunc',
123 coreconfigitem('annotate', 'showfunc',
124 default=False,
124 default=False,
125 )
125 )
126 coreconfigitem('annotate', 'unified',
126 coreconfigitem('annotate', 'unified',
127 default=None,
127 default=None,
128 )
128 )
129 coreconfigitem('annotate', 'git',
129 coreconfigitem('annotate', 'git',
130 default=False,
130 default=False,
131 )
131 )
132 coreconfigitem('annotate', 'ignorews',
132 coreconfigitem('annotate', 'ignorews',
133 default=False,
133 default=False,
134 )
134 )
135 coreconfigitem('annotate', 'ignorewsamount',
135 coreconfigitem('annotate', 'ignorewsamount',
136 default=False,
136 default=False,
137 )
137 )
138 coreconfigitem('annotate', 'ignoreblanklines',
138 coreconfigitem('annotate', 'ignoreblanklines',
139 default=False,
139 default=False,
140 )
140 )
141 coreconfigitem('annotate', 'ignorewseol',
141 coreconfigitem('annotate', 'ignorewseol',
142 default=False,
142 default=False,
143 )
143 )
144 coreconfigitem('annotate', 'nobinary',
144 coreconfigitem('annotate', 'nobinary',
145 default=False,
145 default=False,
146 )
146 )
147 coreconfigitem('annotate', 'noprefix',
147 coreconfigitem('annotate', 'noprefix',
148 default=False,
148 default=False,
149 )
149 )
150 coreconfigitem('auth', 'cookiefile',
150 coreconfigitem('auth', 'cookiefile',
151 default=None,
151 default=None,
152 )
152 )
153 # bookmarks.pushing: internal hack for discovery
153 # bookmarks.pushing: internal hack for discovery
154 coreconfigitem('bookmarks', 'pushing',
154 coreconfigitem('bookmarks', 'pushing',
155 default=list,
155 default=list,
156 )
156 )
157 # bundle.mainreporoot: internal hack for bundlerepo
157 # bundle.mainreporoot: internal hack for bundlerepo
158 coreconfigitem('bundle', 'mainreporoot',
158 coreconfigitem('bundle', 'mainreporoot',
159 default='',
159 default='',
160 )
160 )
161 # bundle.reorder: experimental config
161 # bundle.reorder: experimental config
162 coreconfigitem('bundle', 'reorder',
162 coreconfigitem('bundle', 'reorder',
163 default='auto',
163 default='auto',
164 )
164 )
165 coreconfigitem('censor', 'policy',
165 coreconfigitem('censor', 'policy',
166 default='abort',
166 default='abort',
167 )
167 )
168 coreconfigitem('chgserver', 'idletimeout',
168 coreconfigitem('chgserver', 'idletimeout',
169 default=3600,
169 default=3600,
170 )
170 )
171 coreconfigitem('chgserver', 'skiphash',
171 coreconfigitem('chgserver', 'skiphash',
172 default=False,
172 default=False,
173 )
173 )
174 coreconfigitem('cmdserver', 'log',
174 coreconfigitem('cmdserver', 'log',
175 default=None,
175 default=None,
176 )
176 )
177 coreconfigitem('color', '.*',
177 coreconfigitem('color', '.*',
178 default=None,
178 default=None,
179 generic=True,
179 generic=True,
180 )
180 )
181 coreconfigitem('color', 'mode',
181 coreconfigitem('color', 'mode',
182 default='auto',
182 default='auto',
183 )
183 )
184 coreconfigitem('color', 'pagermode',
184 coreconfigitem('color', 'pagermode',
185 default=dynamicdefault,
185 default=dynamicdefault,
186 )
186 )
187 coreconfigitem('commands', 'show.aliasprefix',
187 coreconfigitem('commands', 'show.aliasprefix',
188 default=list,
188 default=list,
189 )
189 )
190 coreconfigitem('commands', 'status.relative',
190 coreconfigitem('commands', 'status.relative',
191 default=False,
191 default=False,
192 )
192 )
193 coreconfigitem('commands', 'status.skipstates',
193 coreconfigitem('commands', 'status.skipstates',
194 default=[],
194 default=[],
195 )
195 )
196 coreconfigitem('commands', 'status.terse',
196 coreconfigitem('commands', 'status.terse',
197 default='',
197 default='',
198 )
198 )
199 coreconfigitem('commands', 'status.verbose',
199 coreconfigitem('commands', 'status.verbose',
200 default=False,
200 default=False,
201 )
201 )
202 coreconfigitem('commands', 'update.check',
202 coreconfigitem('commands', 'update.check',
203 default=None,
203 default=None,
204 )
204 )
205 coreconfigitem('commands', 'update.requiredest',
205 coreconfigitem('commands', 'update.requiredest',
206 default=False,
206 default=False,
207 )
207 )
208 coreconfigitem('committemplate', '.*',
208 coreconfigitem('committemplate', '.*',
209 default=None,
209 default=None,
210 generic=True,
210 generic=True,
211 )
211 )
212 coreconfigitem('convert', 'cvsps.cache',
212 coreconfigitem('convert', 'cvsps.cache',
213 default=True,
213 default=True,
214 )
214 )
215 coreconfigitem('convert', 'cvsps.fuzz',
215 coreconfigitem('convert', 'cvsps.fuzz',
216 default=60,
216 default=60,
217 )
217 )
218 coreconfigitem('convert', 'cvsps.logencoding',
218 coreconfigitem('convert', 'cvsps.logencoding',
219 default=None,
219 default=None,
220 )
220 )
221 coreconfigitem('convert', 'cvsps.mergefrom',
221 coreconfigitem('convert', 'cvsps.mergefrom',
222 default=None,
222 default=None,
223 )
223 )
224 coreconfigitem('convert', 'cvsps.mergeto',
224 coreconfigitem('convert', 'cvsps.mergeto',
225 default=None,
225 default=None,
226 )
226 )
227 coreconfigitem('convert', 'git.committeractions',
227 coreconfigitem('convert', 'git.committeractions',
228 default=lambda: ['messagedifferent'],
228 default=lambda: ['messagedifferent'],
229 )
229 )
230 coreconfigitem('convert', 'git.extrakeys',
230 coreconfigitem('convert', 'git.extrakeys',
231 default=list,
231 default=list,
232 )
232 )
233 coreconfigitem('convert', 'git.findcopiesharder',
233 coreconfigitem('convert', 'git.findcopiesharder',
234 default=False,
234 default=False,
235 )
235 )
236 coreconfigitem('convert', 'git.remoteprefix',
236 coreconfigitem('convert', 'git.remoteprefix',
237 default='remote',
237 default='remote',
238 )
238 )
239 coreconfigitem('convert', 'git.renamelimit',
239 coreconfigitem('convert', 'git.renamelimit',
240 default=400,
240 default=400,
241 )
241 )
242 coreconfigitem('convert', 'git.saverev',
242 coreconfigitem('convert', 'git.saverev',
243 default=True,
243 default=True,
244 )
244 )
245 coreconfigitem('convert', 'git.similarity',
245 coreconfigitem('convert', 'git.similarity',
246 default=50,
246 default=50,
247 )
247 )
248 coreconfigitem('convert', 'git.skipsubmodules',
248 coreconfigitem('convert', 'git.skipsubmodules',
249 default=False,
249 default=False,
250 )
250 )
251 coreconfigitem('convert', 'hg.clonebranches',
251 coreconfigitem('convert', 'hg.clonebranches',
252 default=False,
252 default=False,
253 )
253 )
254 coreconfigitem('convert', 'hg.ignoreerrors',
254 coreconfigitem('convert', 'hg.ignoreerrors',
255 default=False,
255 default=False,
256 )
256 )
257 coreconfigitem('convert', 'hg.revs',
257 coreconfigitem('convert', 'hg.revs',
258 default=None,
258 default=None,
259 )
259 )
260 coreconfigitem('convert', 'hg.saverev',
260 coreconfigitem('convert', 'hg.saverev',
261 default=False,
261 default=False,
262 )
262 )
263 coreconfigitem('convert', 'hg.sourcename',
263 coreconfigitem('convert', 'hg.sourcename',
264 default=None,
264 default=None,
265 )
265 )
266 coreconfigitem('convert', 'hg.startrev',
266 coreconfigitem('convert', 'hg.startrev',
267 default=None,
267 default=None,
268 )
268 )
269 coreconfigitem('convert', 'hg.tagsbranch',
269 coreconfigitem('convert', 'hg.tagsbranch',
270 default='default',
270 default='default',
271 )
271 )
272 coreconfigitem('convert', 'hg.usebranchnames',
272 coreconfigitem('convert', 'hg.usebranchnames',
273 default=True,
273 default=True,
274 )
274 )
275 coreconfigitem('convert', 'ignoreancestorcheck',
275 coreconfigitem('convert', 'ignoreancestorcheck',
276 default=False,
276 default=False,
277 )
277 )
278 coreconfigitem('convert', 'localtimezone',
278 coreconfigitem('convert', 'localtimezone',
279 default=False,
279 default=False,
280 )
280 )
281 coreconfigitem('convert', 'p4.encoding',
281 coreconfigitem('convert', 'p4.encoding',
282 default=dynamicdefault,
282 default=dynamicdefault,
283 )
283 )
284 coreconfigitem('convert', 'p4.startrev',
284 coreconfigitem('convert', 'p4.startrev',
285 default=0,
285 default=0,
286 )
286 )
287 coreconfigitem('convert', 'skiptags',
287 coreconfigitem('convert', 'skiptags',
288 default=False,
288 default=False,
289 )
289 )
290 coreconfigitem('convert', 'svn.debugsvnlog',
290 coreconfigitem('convert', 'svn.debugsvnlog',
291 default=True,
291 default=True,
292 )
292 )
293 coreconfigitem('convert', 'svn.trunk',
293 coreconfigitem('convert', 'svn.trunk',
294 default=None,
294 default=None,
295 )
295 )
296 coreconfigitem('convert', 'svn.tags',
296 coreconfigitem('convert', 'svn.tags',
297 default=None,
297 default=None,
298 )
298 )
299 coreconfigitem('convert', 'svn.branches',
299 coreconfigitem('convert', 'svn.branches',
300 default=None,
300 default=None,
301 )
301 )
302 coreconfigitem('convert', 'svn.startrev',
302 coreconfigitem('convert', 'svn.startrev',
303 default=0,
303 default=0,
304 )
304 )
305 coreconfigitem('debug', 'dirstate.delaywrite',
305 coreconfigitem('debug', 'dirstate.delaywrite',
306 default=0,
306 default=0,
307 )
307 )
308 coreconfigitem('defaults', '.*',
308 coreconfigitem('defaults', '.*',
309 default=None,
309 default=None,
310 generic=True,
310 generic=True,
311 )
311 )
312 coreconfigitem('devel', 'all-warnings',
312 coreconfigitem('devel', 'all-warnings',
313 default=False,
313 default=False,
314 )
314 )
315 coreconfigitem('devel', 'bundle2.debug',
315 coreconfigitem('devel', 'bundle2.debug',
316 default=False,
316 default=False,
317 )
317 )
318 coreconfigitem('devel', 'cache-vfs',
318 coreconfigitem('devel', 'cache-vfs',
319 default=None,
319 default=None,
320 )
320 )
321 coreconfigitem('devel', 'check-locks',
321 coreconfigitem('devel', 'check-locks',
322 default=False,
322 default=False,
323 )
323 )
324 coreconfigitem('devel', 'check-relroot',
324 coreconfigitem('devel', 'check-relroot',
325 default=False,
325 default=False,
326 )
326 )
327 coreconfigitem('devel', 'default-date',
327 coreconfigitem('devel', 'default-date',
328 default=None,
328 default=None,
329 )
329 )
330 coreconfigitem('devel', 'deprec-warn',
330 coreconfigitem('devel', 'deprec-warn',
331 default=False,
331 default=False,
332 )
332 )
333 coreconfigitem('devel', 'disableloaddefaultcerts',
333 coreconfigitem('devel', 'disableloaddefaultcerts',
334 default=False,
334 default=False,
335 )
335 )
336 coreconfigitem('devel', 'warn-empty-changegroup',
336 coreconfigitem('devel', 'warn-empty-changegroup',
337 default=False,
337 default=False,
338 )
338 )
339 coreconfigitem('devel', 'legacy.exchange',
339 coreconfigitem('devel', 'legacy.exchange',
340 default=list,
340 default=list,
341 )
341 )
342 coreconfigitem('devel', 'servercafile',
342 coreconfigitem('devel', 'servercafile',
343 default='',
343 default='',
344 )
344 )
345 coreconfigitem('devel', 'serverexactprotocol',
345 coreconfigitem('devel', 'serverexactprotocol',
346 default='',
346 default='',
347 )
347 )
348 coreconfigitem('devel', 'serverrequirecert',
348 coreconfigitem('devel', 'serverrequirecert',
349 default=False,
349 default=False,
350 )
350 )
351 coreconfigitem('devel', 'strip-obsmarkers',
351 coreconfigitem('devel', 'strip-obsmarkers',
352 default=True,
352 default=True,
353 )
353 )
354 coreconfigitem('devel', 'warn-config',
354 coreconfigitem('devel', 'warn-config',
355 default=None,
355 default=None,
356 )
356 )
357 coreconfigitem('devel', 'warn-config-default',
357 coreconfigitem('devel', 'warn-config-default',
358 default=None,
358 default=None,
359 )
359 )
360 coreconfigitem('devel', 'user.obsmarker',
360 coreconfigitem('devel', 'user.obsmarker',
361 default=None,
361 default=None,
362 )
362 )
363 coreconfigitem('devel', 'warn-config-unknown',
363 coreconfigitem('devel', 'warn-config-unknown',
364 default=None,
364 default=None,
365 )
365 )
366 coreconfigitem('devel', 'debug.peer-request',
366 coreconfigitem('devel', 'debug.peer-request',
367 default=False,
367 default=False,
368 )
368 )
369 coreconfigitem('diff', 'nodates',
369 coreconfigitem('diff', 'nodates',
370 default=False,
370 default=False,
371 )
371 )
372 coreconfigitem('diff', 'showfunc',
372 coreconfigitem('diff', 'showfunc',
373 default=False,
373 default=False,
374 )
374 )
375 coreconfigitem('diff', 'unified',
375 coreconfigitem('diff', 'unified',
376 default=None,
376 default=None,
377 )
377 )
378 coreconfigitem('diff', 'git',
378 coreconfigitem('diff', 'git',
379 default=False,
379 default=False,
380 )
380 )
381 coreconfigitem('diff', 'ignorews',
381 coreconfigitem('diff', 'ignorews',
382 default=False,
382 default=False,
383 )
383 )
384 coreconfigitem('diff', 'ignorewsamount',
384 coreconfigitem('diff', 'ignorewsamount',
385 default=False,
385 default=False,
386 )
386 )
387 coreconfigitem('diff', 'ignoreblanklines',
387 coreconfigitem('diff', 'ignoreblanklines',
388 default=False,
388 default=False,
389 )
389 )
390 coreconfigitem('diff', 'ignorewseol',
390 coreconfigitem('diff', 'ignorewseol',
391 default=False,
391 default=False,
392 )
392 )
393 coreconfigitem('diff', 'nobinary',
393 coreconfigitem('diff', 'nobinary',
394 default=False,
394 default=False,
395 )
395 )
396 coreconfigitem('diff', 'noprefix',
396 coreconfigitem('diff', 'noprefix',
397 default=False,
397 default=False,
398 )
398 )
399 coreconfigitem('email', 'bcc',
399 coreconfigitem('email', 'bcc',
400 default=None,
400 default=None,
401 )
401 )
402 coreconfigitem('email', 'cc',
402 coreconfigitem('email', 'cc',
403 default=None,
403 default=None,
404 )
404 )
405 coreconfigitem('email', 'charsets',
405 coreconfigitem('email', 'charsets',
406 default=list,
406 default=list,
407 )
407 )
408 coreconfigitem('email', 'from',
408 coreconfigitem('email', 'from',
409 default=None,
409 default=None,
410 )
410 )
411 coreconfigitem('email', 'method',
411 coreconfigitem('email', 'method',
412 default='smtp',
412 default='smtp',
413 )
413 )
414 coreconfigitem('email', 'reply-to',
414 coreconfigitem('email', 'reply-to',
415 default=None,
415 default=None,
416 )
416 )
417 coreconfigitem('email', 'to',
417 coreconfigitem('email', 'to',
418 default=None,
418 default=None,
419 )
419 )
420 coreconfigitem('experimental', 'archivemetatemplate',
420 coreconfigitem('experimental', 'archivemetatemplate',
421 default=dynamicdefault,
421 default=dynamicdefault,
422 )
422 )
423 coreconfigitem('experimental', 'bundle-phases',
423 coreconfigitem('experimental', 'bundle-phases',
424 default=False,
424 default=False,
425 )
425 )
426 coreconfigitem('experimental', 'bundle2-advertise',
426 coreconfigitem('experimental', 'bundle2-advertise',
427 default=True,
427 default=True,
428 )
428 )
429 coreconfigitem('experimental', 'bundle2-output-capture',
429 coreconfigitem('experimental', 'bundle2-output-capture',
430 default=False,
430 default=False,
431 )
431 )
432 coreconfigitem('experimental', 'bundle2.pushback',
432 coreconfigitem('experimental', 'bundle2.pushback',
433 default=False,
433 default=False,
434 )
434 )
435 coreconfigitem('experimental', 'bundle2.stream',
435 coreconfigitem('experimental', 'bundle2.stream',
436 default=False,
436 default=False,
437 )
437 )
438 coreconfigitem('experimental', 'bundle2lazylocking',
438 coreconfigitem('experimental', 'bundle2lazylocking',
439 default=False,
439 default=False,
440 )
440 )
441 coreconfigitem('experimental', 'bundlecomplevel',
441 coreconfigitem('experimental', 'bundlecomplevel',
442 default=None,
442 default=None,
443 )
443 )
444 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
444 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
445 default=None,
445 default=None,
446 )
446 )
447 coreconfigitem('experimental', 'bundlecomplevel.gzip',
447 coreconfigitem('experimental', 'bundlecomplevel.gzip',
448 default=None,
448 default=None,
449 )
449 )
450 coreconfigitem('experimental', 'bundlecomplevel.none',
450 coreconfigitem('experimental', 'bundlecomplevel.none',
451 default=None,
451 default=None,
452 )
452 )
453 coreconfigitem('experimental', 'bundlecomplevel.zstd',
453 coreconfigitem('experimental', 'bundlecomplevel.zstd',
454 default=None,
454 default=None,
455 )
455 )
456 coreconfigitem('experimental', 'changegroup3',
456 coreconfigitem('experimental', 'changegroup3',
457 default=False,
457 default=False,
458 )
458 )
459 coreconfigitem('experimental', 'clientcompressionengines',
459 coreconfigitem('experimental', 'clientcompressionengines',
460 default=list,
460 default=list,
461 )
461 )
462 coreconfigitem('experimental', 'copytrace',
462 coreconfigitem('experimental', 'copytrace',
463 default='on',
463 default='on',
464 )
464 )
465 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
465 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
466 default=100,
466 default=100,
467 )
467 )
468 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
468 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
469 default=100,
469 default=100,
470 )
470 )
471 coreconfigitem('experimental', 'crecordtest',
471 coreconfigitem('experimental', 'crecordtest',
472 default=None,
472 default=None,
473 )
473 )
474 coreconfigitem('experimental', 'directaccess',
474 coreconfigitem('experimental', 'directaccess',
475 default=False,
475 default=False,
476 )
476 )
477 coreconfigitem('experimental', 'directaccess.revnums',
477 coreconfigitem('experimental', 'directaccess.revnums',
478 default=False,
478 default=False,
479 )
479 )
480 coreconfigitem('experimental', 'editortmpinhg',
480 coreconfigitem('experimental', 'editortmpinhg',
481 default=False,
481 default=False,
482 )
482 )
483 coreconfigitem('experimental', 'evolution',
483 coreconfigitem('experimental', 'evolution',
484 default=list,
484 default=list,
485 )
485 )
486 coreconfigitem('experimental', 'evolution.allowdivergence',
486 coreconfigitem('experimental', 'evolution.allowdivergence',
487 default=False,
487 default=False,
488 alias=[('experimental', 'allowdivergence')]
488 alias=[('experimental', 'allowdivergence')]
489 )
489 )
490 coreconfigitem('experimental', 'evolution.allowunstable',
490 coreconfigitem('experimental', 'evolution.allowunstable',
491 default=None,
491 default=None,
492 )
492 )
493 coreconfigitem('experimental', 'evolution.createmarkers',
493 coreconfigitem('experimental', 'evolution.createmarkers',
494 default=None,
494 default=None,
495 )
495 )
496 coreconfigitem('experimental', 'evolution.effect-flags',
496 coreconfigitem('experimental', 'evolution.effect-flags',
497 default=True,
497 default=True,
498 alias=[('experimental', 'effect-flags')]
498 alias=[('experimental', 'effect-flags')]
499 )
499 )
500 coreconfigitem('experimental', 'evolution.exchange',
500 coreconfigitem('experimental', 'evolution.exchange',
501 default=None,
501 default=None,
502 )
502 )
503 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
503 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
504 default=False,
504 default=False,
505 )
505 )
506 coreconfigitem('experimental', 'evolution.report-instabilities',
506 coreconfigitem('experimental', 'evolution.report-instabilities',
507 default=True,
507 default=True,
508 )
508 )
509 coreconfigitem('experimental', 'evolution.track-operation',
509 coreconfigitem('experimental', 'evolution.track-operation',
510 default=True,
510 default=True,
511 )
511 )
512 coreconfigitem('experimental', 'worddiff',
512 coreconfigitem('experimental', 'worddiff',
513 default=False,
513 default=False,
514 )
514 )
515 coreconfigitem('experimental', 'maxdeltachainspan',
515 coreconfigitem('experimental', 'maxdeltachainspan',
516 default=-1,
516 default=-1,
517 )
517 )
518 coreconfigitem('experimental', 'mergetempdirprefix',
518 coreconfigitem('experimental', 'mergetempdirprefix',
519 default=None,
519 default=None,
520 )
520 )
521 coreconfigitem('experimental', 'mmapindexthreshold',
521 coreconfigitem('experimental', 'mmapindexthreshold',
522 default=None,
522 default=None,
523 )
523 )
524 coreconfigitem('experimental', 'nonnormalparanoidcheck',
524 coreconfigitem('experimental', 'nonnormalparanoidcheck',
525 default=False,
525 default=False,
526 )
526 )
527 coreconfigitem('experimental', 'exportableenviron',
527 coreconfigitem('experimental', 'exportableenviron',
528 default=list,
528 default=list,
529 )
529 )
530 coreconfigitem('experimental', 'extendedheader.index',
530 coreconfigitem('experimental', 'extendedheader.index',
531 default=None,
531 default=None,
532 )
532 )
533 coreconfigitem('experimental', 'extendedheader.similarity',
533 coreconfigitem('experimental', 'extendedheader.similarity',
534 default=False,
534 default=False,
535 )
535 )
536 coreconfigitem('experimental', 'format.compression',
536 coreconfigitem('experimental', 'format.compression',
537 default='zlib',
537 default='zlib',
538 )
538 )
539 coreconfigitem('experimental', 'graphshorten',
539 coreconfigitem('experimental', 'graphshorten',
540 default=False,
540 default=False,
541 )
541 )
542 coreconfigitem('experimental', 'graphstyle.parent',
542 coreconfigitem('experimental', 'graphstyle.parent',
543 default=dynamicdefault,
543 default=dynamicdefault,
544 )
544 )
545 coreconfigitem('experimental', 'graphstyle.missing',
545 coreconfigitem('experimental', 'graphstyle.missing',
546 default=dynamicdefault,
546 default=dynamicdefault,
547 )
547 )
548 coreconfigitem('experimental', 'graphstyle.grandparent',
548 coreconfigitem('experimental', 'graphstyle.grandparent',
549 default=dynamicdefault,
549 default=dynamicdefault,
550 )
550 )
551 coreconfigitem('experimental', 'hook-track-tags',
551 coreconfigitem('experimental', 'hook-track-tags',
552 default=False,
552 default=False,
553 )
553 )
554 coreconfigitem('experimental', 'httppeer.advertise-v2',
554 coreconfigitem('experimental', 'httppeer.advertise-v2',
555 default=False,
555 default=False,
556 )
556 )
557 coreconfigitem('experimental', 'httppostargs',
557 coreconfigitem('experimental', 'httppostargs',
558 default=False,
558 default=False,
559 )
559 )
560 coreconfigitem('experimental', 'mergedriver',
560 coreconfigitem('experimental', 'mergedriver',
561 default=None,
561 default=None,
562 )
562 )
563 coreconfigitem('experimental', 'nointerrupt', default=False)
564 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
565
563 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
566 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
564 default=False,
567 default=False,
565 )
568 )
566 coreconfigitem('experimental', 'remotenames',
569 coreconfigitem('experimental', 'remotenames',
567 default=False,
570 default=False,
568 )
571 )
569 coreconfigitem('experimental', 'removeemptydirs',
572 coreconfigitem('experimental', 'removeemptydirs',
570 default=True,
573 default=True,
571 )
574 )
572 coreconfigitem('experimental', 'revlogv2',
575 coreconfigitem('experimental', 'revlogv2',
573 default=None,
576 default=None,
574 )
577 )
575 coreconfigitem('experimental', 'single-head-per-branch',
578 coreconfigitem('experimental', 'single-head-per-branch',
576 default=False,
579 default=False,
577 )
580 )
578 coreconfigitem('experimental', 'sshserver.support-v2',
581 coreconfigitem('experimental', 'sshserver.support-v2',
579 default=False,
582 default=False,
580 )
583 )
581 coreconfigitem('experimental', 'spacemovesdown',
584 coreconfigitem('experimental', 'spacemovesdown',
582 default=False,
585 default=False,
583 )
586 )
584 coreconfigitem('experimental', 'sparse-read',
587 coreconfigitem('experimental', 'sparse-read',
585 default=False,
588 default=False,
586 )
589 )
587 coreconfigitem('experimental', 'sparse-read.density-threshold',
590 coreconfigitem('experimental', 'sparse-read.density-threshold',
588 default=0.25,
591 default=0.25,
589 )
592 )
590 coreconfigitem('experimental', 'sparse-read.min-gap-size',
593 coreconfigitem('experimental', 'sparse-read.min-gap-size',
591 default='256K',
594 default='256K',
592 )
595 )
593 coreconfigitem('experimental', 'treemanifest',
596 coreconfigitem('experimental', 'treemanifest',
594 default=False,
597 default=False,
595 )
598 )
596 coreconfigitem('experimental', 'update.atomic-file',
599 coreconfigitem('experimental', 'update.atomic-file',
597 default=False,
600 default=False,
598 )
601 )
599 coreconfigitem('experimental', 'sshpeer.advertise-v2',
602 coreconfigitem('experimental', 'sshpeer.advertise-v2',
600 default=False,
603 default=False,
601 )
604 )
602 coreconfigitem('experimental', 'web.apiserver',
605 coreconfigitem('experimental', 'web.apiserver',
603 default=False,
606 default=False,
604 )
607 )
605 coreconfigitem('experimental', 'web.api.http-v2',
608 coreconfigitem('experimental', 'web.api.http-v2',
606 default=False,
609 default=False,
607 )
610 )
608 coreconfigitem('experimental', 'web.api.debugreflect',
611 coreconfigitem('experimental', 'web.api.debugreflect',
609 default=False,
612 default=False,
610 )
613 )
611 coreconfigitem('experimental', 'xdiff',
614 coreconfigitem('experimental', 'xdiff',
612 default=False,
615 default=False,
613 )
616 )
614 coreconfigitem('extensions', '.*',
617 coreconfigitem('extensions', '.*',
615 default=None,
618 default=None,
616 generic=True,
619 generic=True,
617 )
620 )
618 coreconfigitem('extdata', '.*',
621 coreconfigitem('extdata', '.*',
619 default=None,
622 default=None,
620 generic=True,
623 generic=True,
621 )
624 )
622 coreconfigitem('format', 'aggressivemergedeltas',
625 coreconfigitem('format', 'aggressivemergedeltas',
623 default=False,
626 default=False,
624 )
627 )
625 coreconfigitem('format', 'chunkcachesize',
628 coreconfigitem('format', 'chunkcachesize',
626 default=None,
629 default=None,
627 )
630 )
628 coreconfigitem('format', 'dotencode',
631 coreconfigitem('format', 'dotencode',
629 default=True,
632 default=True,
630 )
633 )
631 coreconfigitem('format', 'generaldelta',
634 coreconfigitem('format', 'generaldelta',
632 default=False,
635 default=False,
633 )
636 )
634 coreconfigitem('format', 'manifestcachesize',
637 coreconfigitem('format', 'manifestcachesize',
635 default=None,
638 default=None,
636 )
639 )
637 coreconfigitem('format', 'maxchainlen',
640 coreconfigitem('format', 'maxchainlen',
638 default=None,
641 default=None,
639 )
642 )
640 coreconfigitem('format', 'obsstore-version',
643 coreconfigitem('format', 'obsstore-version',
641 default=None,
644 default=None,
642 )
645 )
643 coreconfigitem('format', 'usefncache',
646 coreconfigitem('format', 'usefncache',
644 default=True,
647 default=True,
645 )
648 )
646 coreconfigitem('format', 'usegeneraldelta',
649 coreconfigitem('format', 'usegeneraldelta',
647 default=True,
650 default=True,
648 )
651 )
649 coreconfigitem('format', 'usestore',
652 coreconfigitem('format', 'usestore',
650 default=True,
653 default=True,
651 )
654 )
652 coreconfigitem('fsmonitor', 'warn_when_unused',
655 coreconfigitem('fsmonitor', 'warn_when_unused',
653 default=True,
656 default=True,
654 )
657 )
655 coreconfigitem('fsmonitor', 'warn_update_file_count',
658 coreconfigitem('fsmonitor', 'warn_update_file_count',
656 default=50000,
659 default=50000,
657 )
660 )
658 coreconfigitem('hooks', '.*',
661 coreconfigitem('hooks', '.*',
659 default=dynamicdefault,
662 default=dynamicdefault,
660 generic=True,
663 generic=True,
661 )
664 )
662 coreconfigitem('hgweb-paths', '.*',
665 coreconfigitem('hgweb-paths', '.*',
663 default=list,
666 default=list,
664 generic=True,
667 generic=True,
665 )
668 )
666 coreconfigitem('hostfingerprints', '.*',
669 coreconfigitem('hostfingerprints', '.*',
667 default=list,
670 default=list,
668 generic=True,
671 generic=True,
669 )
672 )
670 coreconfigitem('hostsecurity', 'ciphers',
673 coreconfigitem('hostsecurity', 'ciphers',
671 default=None,
674 default=None,
672 )
675 )
673 coreconfigitem('hostsecurity', 'disabletls10warning',
676 coreconfigitem('hostsecurity', 'disabletls10warning',
674 default=False,
677 default=False,
675 )
678 )
676 coreconfigitem('hostsecurity', 'minimumprotocol',
679 coreconfigitem('hostsecurity', 'minimumprotocol',
677 default=dynamicdefault,
680 default=dynamicdefault,
678 )
681 )
679 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
682 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
680 default=dynamicdefault,
683 default=dynamicdefault,
681 generic=True,
684 generic=True,
682 )
685 )
683 coreconfigitem('hostsecurity', '.*:ciphers$',
686 coreconfigitem('hostsecurity', '.*:ciphers$',
684 default=dynamicdefault,
687 default=dynamicdefault,
685 generic=True,
688 generic=True,
686 )
689 )
687 coreconfigitem('hostsecurity', '.*:fingerprints$',
690 coreconfigitem('hostsecurity', '.*:fingerprints$',
688 default=list,
691 default=list,
689 generic=True,
692 generic=True,
690 )
693 )
691 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
694 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
692 default=None,
695 default=None,
693 generic=True,
696 generic=True,
694 )
697 )
695
698
696 coreconfigitem('http_proxy', 'always',
699 coreconfigitem('http_proxy', 'always',
697 default=False,
700 default=False,
698 )
701 )
699 coreconfigitem('http_proxy', 'host',
702 coreconfigitem('http_proxy', 'host',
700 default=None,
703 default=None,
701 )
704 )
702 coreconfigitem('http_proxy', 'no',
705 coreconfigitem('http_proxy', 'no',
703 default=list,
706 default=list,
704 )
707 )
705 coreconfigitem('http_proxy', 'passwd',
708 coreconfigitem('http_proxy', 'passwd',
706 default=None,
709 default=None,
707 )
710 )
708 coreconfigitem('http_proxy', 'user',
711 coreconfigitem('http_proxy', 'user',
709 default=None,
712 default=None,
710 )
713 )
711 coreconfigitem('logtoprocess', 'commandexception',
714 coreconfigitem('logtoprocess', 'commandexception',
712 default=None,
715 default=None,
713 )
716 )
714 coreconfigitem('logtoprocess', 'commandfinish',
717 coreconfigitem('logtoprocess', 'commandfinish',
715 default=None,
718 default=None,
716 )
719 )
717 coreconfigitem('logtoprocess', 'command',
720 coreconfigitem('logtoprocess', 'command',
718 default=None,
721 default=None,
719 )
722 )
720 coreconfigitem('logtoprocess', 'develwarn',
723 coreconfigitem('logtoprocess', 'develwarn',
721 default=None,
724 default=None,
722 )
725 )
723 coreconfigitem('logtoprocess', 'uiblocked',
726 coreconfigitem('logtoprocess', 'uiblocked',
724 default=None,
727 default=None,
725 )
728 )
726 coreconfigitem('merge', 'checkunknown',
729 coreconfigitem('merge', 'checkunknown',
727 default='abort',
730 default='abort',
728 )
731 )
729 coreconfigitem('merge', 'checkignored',
732 coreconfigitem('merge', 'checkignored',
730 default='abort',
733 default='abort',
731 )
734 )
732 coreconfigitem('experimental', 'merge.checkpathconflicts',
735 coreconfigitem('experimental', 'merge.checkpathconflicts',
733 default=False,
736 default=False,
734 )
737 )
735 coreconfigitem('merge', 'followcopies',
738 coreconfigitem('merge', 'followcopies',
736 default=True,
739 default=True,
737 )
740 )
738 coreconfigitem('merge', 'on-failure',
741 coreconfigitem('merge', 'on-failure',
739 default='continue',
742 default='continue',
740 )
743 )
741 coreconfigitem('merge', 'preferancestor',
744 coreconfigitem('merge', 'preferancestor',
742 default=lambda: ['*'],
745 default=lambda: ['*'],
743 )
746 )
744 coreconfigitem('merge-tools', '.*',
747 coreconfigitem('merge-tools', '.*',
745 default=None,
748 default=None,
746 generic=True,
749 generic=True,
747 )
750 )
748 coreconfigitem('merge-tools', br'.*\.args$',
751 coreconfigitem('merge-tools', br'.*\.args$',
749 default="$local $base $other",
752 default="$local $base $other",
750 generic=True,
753 generic=True,
751 priority=-1,
754 priority=-1,
752 )
755 )
753 coreconfigitem('merge-tools', br'.*\.binary$',
756 coreconfigitem('merge-tools', br'.*\.binary$',
754 default=False,
757 default=False,
755 generic=True,
758 generic=True,
756 priority=-1,
759 priority=-1,
757 )
760 )
758 coreconfigitem('merge-tools', br'.*\.check$',
761 coreconfigitem('merge-tools', br'.*\.check$',
759 default=list,
762 default=list,
760 generic=True,
763 generic=True,
761 priority=-1,
764 priority=-1,
762 )
765 )
763 coreconfigitem('merge-tools', br'.*\.checkchanged$',
766 coreconfigitem('merge-tools', br'.*\.checkchanged$',
764 default=False,
767 default=False,
765 generic=True,
768 generic=True,
766 priority=-1,
769 priority=-1,
767 )
770 )
768 coreconfigitem('merge-tools', br'.*\.executable$',
771 coreconfigitem('merge-tools', br'.*\.executable$',
769 default=dynamicdefault,
772 default=dynamicdefault,
770 generic=True,
773 generic=True,
771 priority=-1,
774 priority=-1,
772 )
775 )
773 coreconfigitem('merge-tools', br'.*\.fixeol$',
776 coreconfigitem('merge-tools', br'.*\.fixeol$',
774 default=False,
777 default=False,
775 generic=True,
778 generic=True,
776 priority=-1,
779 priority=-1,
777 )
780 )
778 coreconfigitem('merge-tools', br'.*\.gui$',
781 coreconfigitem('merge-tools', br'.*\.gui$',
779 default=False,
782 default=False,
780 generic=True,
783 generic=True,
781 priority=-1,
784 priority=-1,
782 )
785 )
783 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
786 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
784 default='basic',
787 default='basic',
785 generic=True,
788 generic=True,
786 priority=-1,
789 priority=-1,
787 )
790 )
788 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
791 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
789 default=dynamicdefault, # take from ui.mergemarkertemplate
792 default=dynamicdefault, # take from ui.mergemarkertemplate
790 generic=True,
793 generic=True,
791 priority=-1,
794 priority=-1,
792 )
795 )
793 coreconfigitem('merge-tools', br'.*\.priority$',
796 coreconfigitem('merge-tools', br'.*\.priority$',
794 default=0,
797 default=0,
795 generic=True,
798 generic=True,
796 priority=-1,
799 priority=-1,
797 )
800 )
798 coreconfigitem('merge-tools', br'.*\.premerge$',
801 coreconfigitem('merge-tools', br'.*\.premerge$',
799 default=dynamicdefault,
802 default=dynamicdefault,
800 generic=True,
803 generic=True,
801 priority=-1,
804 priority=-1,
802 )
805 )
803 coreconfigitem('merge-tools', br'.*\.symlink$',
806 coreconfigitem('merge-tools', br'.*\.symlink$',
804 default=False,
807 default=False,
805 generic=True,
808 generic=True,
806 priority=-1,
809 priority=-1,
807 )
810 )
808 coreconfigitem('pager', 'attend-.*',
811 coreconfigitem('pager', 'attend-.*',
809 default=dynamicdefault,
812 default=dynamicdefault,
810 generic=True,
813 generic=True,
811 )
814 )
812 coreconfigitem('pager', 'ignore',
815 coreconfigitem('pager', 'ignore',
813 default=list,
816 default=list,
814 )
817 )
815 coreconfigitem('pager', 'pager',
818 coreconfigitem('pager', 'pager',
816 default=dynamicdefault,
819 default=dynamicdefault,
817 )
820 )
818 coreconfigitem('patch', 'eol',
821 coreconfigitem('patch', 'eol',
819 default='strict',
822 default='strict',
820 )
823 )
821 coreconfigitem('patch', 'fuzz',
824 coreconfigitem('patch', 'fuzz',
822 default=2,
825 default=2,
823 )
826 )
824 coreconfigitem('paths', 'default',
827 coreconfigitem('paths', 'default',
825 default=None,
828 default=None,
826 )
829 )
827 coreconfigitem('paths', 'default-push',
830 coreconfigitem('paths', 'default-push',
828 default=None,
831 default=None,
829 )
832 )
830 coreconfigitem('paths', '.*',
833 coreconfigitem('paths', '.*',
831 default=None,
834 default=None,
832 generic=True,
835 generic=True,
833 )
836 )
834 coreconfigitem('phases', 'checksubrepos',
837 coreconfigitem('phases', 'checksubrepos',
835 default='follow',
838 default='follow',
836 )
839 )
837 coreconfigitem('phases', 'new-commit',
840 coreconfigitem('phases', 'new-commit',
838 default='draft',
841 default='draft',
839 )
842 )
840 coreconfigitem('phases', 'publish',
843 coreconfigitem('phases', 'publish',
841 default=True,
844 default=True,
842 )
845 )
843 coreconfigitem('profiling', 'enabled',
846 coreconfigitem('profiling', 'enabled',
844 default=False,
847 default=False,
845 )
848 )
846 coreconfigitem('profiling', 'format',
849 coreconfigitem('profiling', 'format',
847 default='text',
850 default='text',
848 )
851 )
849 coreconfigitem('profiling', 'freq',
852 coreconfigitem('profiling', 'freq',
850 default=1000,
853 default=1000,
851 )
854 )
852 coreconfigitem('profiling', 'limit',
855 coreconfigitem('profiling', 'limit',
853 default=30,
856 default=30,
854 )
857 )
855 coreconfigitem('profiling', 'nested',
858 coreconfigitem('profiling', 'nested',
856 default=0,
859 default=0,
857 )
860 )
858 coreconfigitem('profiling', 'output',
861 coreconfigitem('profiling', 'output',
859 default=None,
862 default=None,
860 )
863 )
861 coreconfigitem('profiling', 'showmax',
864 coreconfigitem('profiling', 'showmax',
862 default=0.999,
865 default=0.999,
863 )
866 )
864 coreconfigitem('profiling', 'showmin',
867 coreconfigitem('profiling', 'showmin',
865 default=dynamicdefault,
868 default=dynamicdefault,
866 )
869 )
867 coreconfigitem('profiling', 'sort',
870 coreconfigitem('profiling', 'sort',
868 default='inlinetime',
871 default='inlinetime',
869 )
872 )
870 coreconfigitem('profiling', 'statformat',
873 coreconfigitem('profiling', 'statformat',
871 default='hotpath',
874 default='hotpath',
872 )
875 )
873 coreconfigitem('profiling', 'time-track',
876 coreconfigitem('profiling', 'time-track',
874 default='cpu',
877 default='cpu',
875 )
878 )
876 coreconfigitem('profiling', 'type',
879 coreconfigitem('profiling', 'type',
877 default='stat',
880 default='stat',
878 )
881 )
879 coreconfigitem('progress', 'assume-tty',
882 coreconfigitem('progress', 'assume-tty',
880 default=False,
883 default=False,
881 )
884 )
882 coreconfigitem('progress', 'changedelay',
885 coreconfigitem('progress', 'changedelay',
883 default=1,
886 default=1,
884 )
887 )
885 coreconfigitem('progress', 'clear-complete',
888 coreconfigitem('progress', 'clear-complete',
886 default=True,
889 default=True,
887 )
890 )
888 coreconfigitem('progress', 'debug',
891 coreconfigitem('progress', 'debug',
889 default=False,
892 default=False,
890 )
893 )
891 coreconfigitem('progress', 'delay',
894 coreconfigitem('progress', 'delay',
892 default=3,
895 default=3,
893 )
896 )
894 coreconfigitem('progress', 'disable',
897 coreconfigitem('progress', 'disable',
895 default=False,
898 default=False,
896 )
899 )
897 coreconfigitem('progress', 'estimateinterval',
900 coreconfigitem('progress', 'estimateinterval',
898 default=60.0,
901 default=60.0,
899 )
902 )
900 coreconfigitem('progress', 'format',
903 coreconfigitem('progress', 'format',
901 default=lambda: ['topic', 'bar', 'number', 'estimate'],
904 default=lambda: ['topic', 'bar', 'number', 'estimate'],
902 )
905 )
903 coreconfigitem('progress', 'refresh',
906 coreconfigitem('progress', 'refresh',
904 default=0.1,
907 default=0.1,
905 )
908 )
906 coreconfigitem('progress', 'width',
909 coreconfigitem('progress', 'width',
907 default=dynamicdefault,
910 default=dynamicdefault,
908 )
911 )
909 coreconfigitem('push', 'pushvars.server',
912 coreconfigitem('push', 'pushvars.server',
910 default=False,
913 default=False,
911 )
914 )
912 coreconfigitem('server', 'bookmarks-pushkey-compat',
915 coreconfigitem('server', 'bookmarks-pushkey-compat',
913 default=True,
916 default=True,
914 )
917 )
915 coreconfigitem('server', 'bundle1',
918 coreconfigitem('server', 'bundle1',
916 default=True,
919 default=True,
917 )
920 )
918 coreconfigitem('server', 'bundle1gd',
921 coreconfigitem('server', 'bundle1gd',
919 default=None,
922 default=None,
920 )
923 )
921 coreconfigitem('server', 'bundle1.pull',
924 coreconfigitem('server', 'bundle1.pull',
922 default=None,
925 default=None,
923 )
926 )
924 coreconfigitem('server', 'bundle1gd.pull',
927 coreconfigitem('server', 'bundle1gd.pull',
925 default=None,
928 default=None,
926 )
929 )
927 coreconfigitem('server', 'bundle1.push',
930 coreconfigitem('server', 'bundle1.push',
928 default=None,
931 default=None,
929 )
932 )
930 coreconfigitem('server', 'bundle1gd.push',
933 coreconfigitem('server', 'bundle1gd.push',
931 default=None,
934 default=None,
932 )
935 )
933 coreconfigitem('server', 'compressionengines',
936 coreconfigitem('server', 'compressionengines',
934 default=list,
937 default=list,
935 )
938 )
936 coreconfigitem('server', 'concurrent-push-mode',
939 coreconfigitem('server', 'concurrent-push-mode',
937 default='strict',
940 default='strict',
938 )
941 )
939 coreconfigitem('server', 'disablefullbundle',
942 coreconfigitem('server', 'disablefullbundle',
940 default=False,
943 default=False,
941 )
944 )
942 coreconfigitem('server', 'maxhttpheaderlen',
945 coreconfigitem('server', 'maxhttpheaderlen',
943 default=1024,
946 default=1024,
944 )
947 )
945 coreconfigitem('server', 'pullbundle',
948 coreconfigitem('server', 'pullbundle',
946 default=False,
949 default=False,
947 )
950 )
948 coreconfigitem('server', 'preferuncompressed',
951 coreconfigitem('server', 'preferuncompressed',
949 default=False,
952 default=False,
950 )
953 )
951 coreconfigitem('server', 'streamunbundle',
954 coreconfigitem('server', 'streamunbundle',
952 default=False,
955 default=False,
953 )
956 )
954 coreconfigitem('server', 'uncompressed',
957 coreconfigitem('server', 'uncompressed',
955 default=True,
958 default=True,
956 )
959 )
957 coreconfigitem('server', 'uncompressedallowsecret',
960 coreconfigitem('server', 'uncompressedallowsecret',
958 default=False,
961 default=False,
959 )
962 )
960 coreconfigitem('server', 'validate',
963 coreconfigitem('server', 'validate',
961 default=False,
964 default=False,
962 )
965 )
963 coreconfigitem('server', 'zliblevel',
966 coreconfigitem('server', 'zliblevel',
964 default=-1,
967 default=-1,
965 )
968 )
966 coreconfigitem('server', 'zstdlevel',
969 coreconfigitem('server', 'zstdlevel',
967 default=3,
970 default=3,
968 )
971 )
969 coreconfigitem('share', 'pool',
972 coreconfigitem('share', 'pool',
970 default=None,
973 default=None,
971 )
974 )
972 coreconfigitem('share', 'poolnaming',
975 coreconfigitem('share', 'poolnaming',
973 default='identity',
976 default='identity',
974 )
977 )
975 coreconfigitem('smtp', 'host',
978 coreconfigitem('smtp', 'host',
976 default=None,
979 default=None,
977 )
980 )
978 coreconfigitem('smtp', 'local_hostname',
981 coreconfigitem('smtp', 'local_hostname',
979 default=None,
982 default=None,
980 )
983 )
981 coreconfigitem('smtp', 'password',
984 coreconfigitem('smtp', 'password',
982 default=None,
985 default=None,
983 )
986 )
984 coreconfigitem('smtp', 'port',
987 coreconfigitem('smtp', 'port',
985 default=dynamicdefault,
988 default=dynamicdefault,
986 )
989 )
987 coreconfigitem('smtp', 'tls',
990 coreconfigitem('smtp', 'tls',
988 default='none',
991 default='none',
989 )
992 )
990 coreconfigitem('smtp', 'username',
993 coreconfigitem('smtp', 'username',
991 default=None,
994 default=None,
992 )
995 )
993 coreconfigitem('sparse', 'missingwarning',
996 coreconfigitem('sparse', 'missingwarning',
994 default=True,
997 default=True,
995 )
998 )
996 coreconfigitem('subrepos', 'allowed',
999 coreconfigitem('subrepos', 'allowed',
997 default=dynamicdefault, # to make backporting simpler
1000 default=dynamicdefault, # to make backporting simpler
998 )
1001 )
999 coreconfigitem('subrepos', 'hg:allowed',
1002 coreconfigitem('subrepos', 'hg:allowed',
1000 default=dynamicdefault,
1003 default=dynamicdefault,
1001 )
1004 )
1002 coreconfigitem('subrepos', 'git:allowed',
1005 coreconfigitem('subrepos', 'git:allowed',
1003 default=dynamicdefault,
1006 default=dynamicdefault,
1004 )
1007 )
1005 coreconfigitem('subrepos', 'svn:allowed',
1008 coreconfigitem('subrepos', 'svn:allowed',
1006 default=dynamicdefault,
1009 default=dynamicdefault,
1007 )
1010 )
1008 coreconfigitem('templates', '.*',
1011 coreconfigitem('templates', '.*',
1009 default=None,
1012 default=None,
1010 generic=True,
1013 generic=True,
1011 )
1014 )
1012 coreconfigitem('trusted', 'groups',
1015 coreconfigitem('trusted', 'groups',
1013 default=list,
1016 default=list,
1014 )
1017 )
1015 coreconfigitem('trusted', 'users',
1018 coreconfigitem('trusted', 'users',
1016 default=list,
1019 default=list,
1017 )
1020 )
1018 coreconfigitem('ui', '_usedassubrepo',
1021 coreconfigitem('ui', '_usedassubrepo',
1019 default=False,
1022 default=False,
1020 )
1023 )
1021 coreconfigitem('ui', 'allowemptycommit',
1024 coreconfigitem('ui', 'allowemptycommit',
1022 default=False,
1025 default=False,
1023 )
1026 )
1024 coreconfigitem('ui', 'archivemeta',
1027 coreconfigitem('ui', 'archivemeta',
1025 default=True,
1028 default=True,
1026 )
1029 )
1027 coreconfigitem('ui', 'askusername',
1030 coreconfigitem('ui', 'askusername',
1028 default=False,
1031 default=False,
1029 )
1032 )
1030 coreconfigitem('ui', 'clonebundlefallback',
1033 coreconfigitem('ui', 'clonebundlefallback',
1031 default=False,
1034 default=False,
1032 )
1035 )
1033 coreconfigitem('ui', 'clonebundleprefers',
1036 coreconfigitem('ui', 'clonebundleprefers',
1034 default=list,
1037 default=list,
1035 )
1038 )
1036 coreconfigitem('ui', 'clonebundles',
1039 coreconfigitem('ui', 'clonebundles',
1037 default=True,
1040 default=True,
1038 )
1041 )
1039 coreconfigitem('ui', 'color',
1042 coreconfigitem('ui', 'color',
1040 default='auto',
1043 default='auto',
1041 )
1044 )
1042 coreconfigitem('ui', 'commitsubrepos',
1045 coreconfigitem('ui', 'commitsubrepos',
1043 default=False,
1046 default=False,
1044 )
1047 )
1045 coreconfigitem('ui', 'debug',
1048 coreconfigitem('ui', 'debug',
1046 default=False,
1049 default=False,
1047 )
1050 )
1048 coreconfigitem('ui', 'debugger',
1051 coreconfigitem('ui', 'debugger',
1049 default=None,
1052 default=None,
1050 )
1053 )
1051 coreconfigitem('ui', 'editor',
1054 coreconfigitem('ui', 'editor',
1052 default=dynamicdefault,
1055 default=dynamicdefault,
1053 )
1056 )
1054 coreconfigitem('ui', 'fallbackencoding',
1057 coreconfigitem('ui', 'fallbackencoding',
1055 default=None,
1058 default=None,
1056 )
1059 )
1057 coreconfigitem('ui', 'forcecwd',
1060 coreconfigitem('ui', 'forcecwd',
1058 default=None,
1061 default=None,
1059 )
1062 )
1060 coreconfigitem('ui', 'forcemerge',
1063 coreconfigitem('ui', 'forcemerge',
1061 default=None,
1064 default=None,
1062 )
1065 )
1063 coreconfigitem('ui', 'formatdebug',
1066 coreconfigitem('ui', 'formatdebug',
1064 default=False,
1067 default=False,
1065 )
1068 )
1066 coreconfigitem('ui', 'formatjson',
1069 coreconfigitem('ui', 'formatjson',
1067 default=False,
1070 default=False,
1068 )
1071 )
1069 coreconfigitem('ui', 'formatted',
1072 coreconfigitem('ui', 'formatted',
1070 default=None,
1073 default=None,
1071 )
1074 )
1072 coreconfigitem('ui', 'graphnodetemplate',
1075 coreconfigitem('ui', 'graphnodetemplate',
1073 default=None,
1076 default=None,
1074 )
1077 )
1075 coreconfigitem('ui', 'interactive',
1078 coreconfigitem('ui', 'interactive',
1076 default=None,
1079 default=None,
1077 )
1080 )
1078 coreconfigitem('ui', 'interface',
1081 coreconfigitem('ui', 'interface',
1079 default=None,
1082 default=None,
1080 )
1083 )
1081 coreconfigitem('ui', 'interface.chunkselector',
1084 coreconfigitem('ui', 'interface.chunkselector',
1082 default=None,
1085 default=None,
1083 )
1086 )
1084 coreconfigitem('ui', 'logblockedtimes',
1087 coreconfigitem('ui', 'logblockedtimes',
1085 default=False,
1088 default=False,
1086 )
1089 )
1087 coreconfigitem('ui', 'logtemplate',
1090 coreconfigitem('ui', 'logtemplate',
1088 default=None,
1091 default=None,
1089 )
1092 )
1090 coreconfigitem('ui', 'merge',
1093 coreconfigitem('ui', 'merge',
1091 default=None,
1094 default=None,
1092 )
1095 )
1093 coreconfigitem('ui', 'mergemarkers',
1096 coreconfigitem('ui', 'mergemarkers',
1094 default='basic',
1097 default='basic',
1095 )
1098 )
1096 coreconfigitem('ui', 'mergemarkertemplate',
1099 coreconfigitem('ui', 'mergemarkertemplate',
1097 default=('{node|short} '
1100 default=('{node|short} '
1098 '{ifeq(tags, "tip", "", '
1101 '{ifeq(tags, "tip", "", '
1099 'ifeq(tags, "", "", "{tags} "))}'
1102 'ifeq(tags, "", "", "{tags} "))}'
1100 '{if(bookmarks, "{bookmarks} ")}'
1103 '{if(bookmarks, "{bookmarks} ")}'
1101 '{ifeq(branch, "default", "", "{branch} ")}'
1104 '{ifeq(branch, "default", "", "{branch} ")}'
1102 '- {author|user}: {desc|firstline}')
1105 '- {author|user}: {desc|firstline}')
1103 )
1106 )
1104 coreconfigitem('ui', 'nontty',
1107 coreconfigitem('ui', 'nontty',
1105 default=False,
1108 default=False,
1106 )
1109 )
1107 coreconfigitem('ui', 'origbackuppath',
1110 coreconfigitem('ui', 'origbackuppath',
1108 default=None,
1111 default=None,
1109 )
1112 )
1110 coreconfigitem('ui', 'paginate',
1113 coreconfigitem('ui', 'paginate',
1111 default=True,
1114 default=True,
1112 )
1115 )
1113 coreconfigitem('ui', 'patch',
1116 coreconfigitem('ui', 'patch',
1114 default=None,
1117 default=None,
1115 )
1118 )
1116 coreconfigitem('ui', 'portablefilenames',
1119 coreconfigitem('ui', 'portablefilenames',
1117 default='warn',
1120 default='warn',
1118 )
1121 )
1119 coreconfigitem('ui', 'promptecho',
1122 coreconfigitem('ui', 'promptecho',
1120 default=False,
1123 default=False,
1121 )
1124 )
1122 coreconfigitem('ui', 'quiet',
1125 coreconfigitem('ui', 'quiet',
1123 default=False,
1126 default=False,
1124 )
1127 )
1125 coreconfigitem('ui', 'quietbookmarkmove',
1128 coreconfigitem('ui', 'quietbookmarkmove',
1126 default=False,
1129 default=False,
1127 )
1130 )
1128 coreconfigitem('ui', 'remotecmd',
1131 coreconfigitem('ui', 'remotecmd',
1129 default='hg',
1132 default='hg',
1130 )
1133 )
1131 coreconfigitem('ui', 'report_untrusted',
1134 coreconfigitem('ui', 'report_untrusted',
1132 default=True,
1135 default=True,
1133 )
1136 )
1134 coreconfigitem('ui', 'rollback',
1137 coreconfigitem('ui', 'rollback',
1135 default=True,
1138 default=True,
1136 )
1139 )
1137 coreconfigitem('ui', 'signal-safe-lock',
1140 coreconfigitem('ui', 'signal-safe-lock',
1138 default=True,
1141 default=True,
1139 )
1142 )
1140 coreconfigitem('ui', 'slash',
1143 coreconfigitem('ui', 'slash',
1141 default=False,
1144 default=False,
1142 )
1145 )
1143 coreconfigitem('ui', 'ssh',
1146 coreconfigitem('ui', 'ssh',
1144 default='ssh',
1147 default='ssh',
1145 )
1148 )
1146 coreconfigitem('ui', 'ssherrorhint',
1149 coreconfigitem('ui', 'ssherrorhint',
1147 default=None,
1150 default=None,
1148 )
1151 )
1149 coreconfigitem('ui', 'statuscopies',
1152 coreconfigitem('ui', 'statuscopies',
1150 default=False,
1153 default=False,
1151 )
1154 )
1152 coreconfigitem('ui', 'strict',
1155 coreconfigitem('ui', 'strict',
1153 default=False,
1156 default=False,
1154 )
1157 )
1155 coreconfigitem('ui', 'style',
1158 coreconfigitem('ui', 'style',
1156 default='',
1159 default='',
1157 )
1160 )
1158 coreconfigitem('ui', 'supportcontact',
1161 coreconfigitem('ui', 'supportcontact',
1159 default=None,
1162 default=None,
1160 )
1163 )
1161 coreconfigitem('ui', 'textwidth',
1164 coreconfigitem('ui', 'textwidth',
1162 default=78,
1165 default=78,
1163 )
1166 )
1164 coreconfigitem('ui', 'timeout',
1167 coreconfigitem('ui', 'timeout',
1165 default='600',
1168 default='600',
1166 )
1169 )
1167 coreconfigitem('ui', 'timeout.warn',
1170 coreconfigitem('ui', 'timeout.warn',
1168 default=0,
1171 default=0,
1169 )
1172 )
1170 coreconfigitem('ui', 'traceback',
1173 coreconfigitem('ui', 'traceback',
1171 default=False,
1174 default=False,
1172 )
1175 )
1173 coreconfigitem('ui', 'tweakdefaults',
1176 coreconfigitem('ui', 'tweakdefaults',
1174 default=False,
1177 default=False,
1175 )
1178 )
1176 coreconfigitem('ui', 'username',
1179 coreconfigitem('ui', 'username',
1177 alias=[('ui', 'user')]
1180 alias=[('ui', 'user')]
1178 )
1181 )
1179 coreconfigitem('ui', 'verbose',
1182 coreconfigitem('ui', 'verbose',
1180 default=False,
1183 default=False,
1181 )
1184 )
1182 coreconfigitem('verify', 'skipflags',
1185 coreconfigitem('verify', 'skipflags',
1183 default=None,
1186 default=None,
1184 )
1187 )
1185 coreconfigitem('web', 'allowbz2',
1188 coreconfigitem('web', 'allowbz2',
1186 default=False,
1189 default=False,
1187 )
1190 )
1188 coreconfigitem('web', 'allowgz',
1191 coreconfigitem('web', 'allowgz',
1189 default=False,
1192 default=False,
1190 )
1193 )
1191 coreconfigitem('web', 'allow-pull',
1194 coreconfigitem('web', 'allow-pull',
1192 alias=[('web', 'allowpull')],
1195 alias=[('web', 'allowpull')],
1193 default=True,
1196 default=True,
1194 )
1197 )
1195 coreconfigitem('web', 'allow-push',
1198 coreconfigitem('web', 'allow-push',
1196 alias=[('web', 'allow_push')],
1199 alias=[('web', 'allow_push')],
1197 default=list,
1200 default=list,
1198 )
1201 )
1199 coreconfigitem('web', 'allowzip',
1202 coreconfigitem('web', 'allowzip',
1200 default=False,
1203 default=False,
1201 )
1204 )
1202 coreconfigitem('web', 'archivesubrepos',
1205 coreconfigitem('web', 'archivesubrepos',
1203 default=False,
1206 default=False,
1204 )
1207 )
1205 coreconfigitem('web', 'cache',
1208 coreconfigitem('web', 'cache',
1206 default=True,
1209 default=True,
1207 )
1210 )
1208 coreconfigitem('web', 'contact',
1211 coreconfigitem('web', 'contact',
1209 default=None,
1212 default=None,
1210 )
1213 )
1211 coreconfigitem('web', 'deny_push',
1214 coreconfigitem('web', 'deny_push',
1212 default=list,
1215 default=list,
1213 )
1216 )
1214 coreconfigitem('web', 'guessmime',
1217 coreconfigitem('web', 'guessmime',
1215 default=False,
1218 default=False,
1216 )
1219 )
1217 coreconfigitem('web', 'hidden',
1220 coreconfigitem('web', 'hidden',
1218 default=False,
1221 default=False,
1219 )
1222 )
1220 coreconfigitem('web', 'labels',
1223 coreconfigitem('web', 'labels',
1221 default=list,
1224 default=list,
1222 )
1225 )
1223 coreconfigitem('web', 'logoimg',
1226 coreconfigitem('web', 'logoimg',
1224 default='hglogo.png',
1227 default='hglogo.png',
1225 )
1228 )
1226 coreconfigitem('web', 'logourl',
1229 coreconfigitem('web', 'logourl',
1227 default='https://mercurial-scm.org/',
1230 default='https://mercurial-scm.org/',
1228 )
1231 )
1229 coreconfigitem('web', 'accesslog',
1232 coreconfigitem('web', 'accesslog',
1230 default='-',
1233 default='-',
1231 )
1234 )
1232 coreconfigitem('web', 'address',
1235 coreconfigitem('web', 'address',
1233 default='',
1236 default='',
1234 )
1237 )
1235 coreconfigitem('web', 'allow-archive',
1238 coreconfigitem('web', 'allow-archive',
1236 alias=[('web', 'allow_archive')],
1239 alias=[('web', 'allow_archive')],
1237 default=list,
1240 default=list,
1238 )
1241 )
1239 coreconfigitem('web', 'allow_read',
1242 coreconfigitem('web', 'allow_read',
1240 default=list,
1243 default=list,
1241 )
1244 )
1242 coreconfigitem('web', 'baseurl',
1245 coreconfigitem('web', 'baseurl',
1243 default=None,
1246 default=None,
1244 )
1247 )
1245 coreconfigitem('web', 'cacerts',
1248 coreconfigitem('web', 'cacerts',
1246 default=None,
1249 default=None,
1247 )
1250 )
1248 coreconfigitem('web', 'certificate',
1251 coreconfigitem('web', 'certificate',
1249 default=None,
1252 default=None,
1250 )
1253 )
1251 coreconfigitem('web', 'collapse',
1254 coreconfigitem('web', 'collapse',
1252 default=False,
1255 default=False,
1253 )
1256 )
1254 coreconfigitem('web', 'csp',
1257 coreconfigitem('web', 'csp',
1255 default=None,
1258 default=None,
1256 )
1259 )
1257 coreconfigitem('web', 'deny_read',
1260 coreconfigitem('web', 'deny_read',
1258 default=list,
1261 default=list,
1259 )
1262 )
1260 coreconfigitem('web', 'descend',
1263 coreconfigitem('web', 'descend',
1261 default=True,
1264 default=True,
1262 )
1265 )
1263 coreconfigitem('web', 'description',
1266 coreconfigitem('web', 'description',
1264 default="",
1267 default="",
1265 )
1268 )
1266 coreconfigitem('web', 'encoding',
1269 coreconfigitem('web', 'encoding',
1267 default=lambda: encoding.encoding,
1270 default=lambda: encoding.encoding,
1268 )
1271 )
1269 coreconfigitem('web', 'errorlog',
1272 coreconfigitem('web', 'errorlog',
1270 default='-',
1273 default='-',
1271 )
1274 )
1272 coreconfigitem('web', 'ipv6',
1275 coreconfigitem('web', 'ipv6',
1273 default=False,
1276 default=False,
1274 )
1277 )
1275 coreconfigitem('web', 'maxchanges',
1278 coreconfigitem('web', 'maxchanges',
1276 default=10,
1279 default=10,
1277 )
1280 )
1278 coreconfigitem('web', 'maxfiles',
1281 coreconfigitem('web', 'maxfiles',
1279 default=10,
1282 default=10,
1280 )
1283 )
1281 coreconfigitem('web', 'maxshortchanges',
1284 coreconfigitem('web', 'maxshortchanges',
1282 default=60,
1285 default=60,
1283 )
1286 )
1284 coreconfigitem('web', 'motd',
1287 coreconfigitem('web', 'motd',
1285 default='',
1288 default='',
1286 )
1289 )
1287 coreconfigitem('web', 'name',
1290 coreconfigitem('web', 'name',
1288 default=dynamicdefault,
1291 default=dynamicdefault,
1289 )
1292 )
1290 coreconfigitem('web', 'port',
1293 coreconfigitem('web', 'port',
1291 default=8000,
1294 default=8000,
1292 )
1295 )
1293 coreconfigitem('web', 'prefix',
1296 coreconfigitem('web', 'prefix',
1294 default='',
1297 default='',
1295 )
1298 )
1296 coreconfigitem('web', 'push_ssl',
1299 coreconfigitem('web', 'push_ssl',
1297 default=True,
1300 default=True,
1298 )
1301 )
1299 coreconfigitem('web', 'refreshinterval',
1302 coreconfigitem('web', 'refreshinterval',
1300 default=20,
1303 default=20,
1301 )
1304 )
1302 coreconfigitem('web', 'server-header',
1305 coreconfigitem('web', 'server-header',
1303 default=None,
1306 default=None,
1304 )
1307 )
1305 coreconfigitem('web', 'staticurl',
1308 coreconfigitem('web', 'staticurl',
1306 default=None,
1309 default=None,
1307 )
1310 )
1308 coreconfigitem('web', 'stripes',
1311 coreconfigitem('web', 'stripes',
1309 default=1,
1312 default=1,
1310 )
1313 )
1311 coreconfigitem('web', 'style',
1314 coreconfigitem('web', 'style',
1312 default='paper',
1315 default='paper',
1313 )
1316 )
1314 coreconfigitem('web', 'templates',
1317 coreconfigitem('web', 'templates',
1315 default=None,
1318 default=None,
1316 )
1319 )
1317 coreconfigitem('web', 'view',
1320 coreconfigitem('web', 'view',
1318 default='served',
1321 default='served',
1319 )
1322 )
1320 coreconfigitem('worker', 'backgroundclose',
1323 coreconfigitem('worker', 'backgroundclose',
1321 default=dynamicdefault,
1324 default=dynamicdefault,
1322 )
1325 )
1323 # Windows defaults to a limit of 512 open files. A buffer of 128
1326 # Windows defaults to a limit of 512 open files. A buffer of 128
1324 # should give us enough headway.
1327 # should give us enough headway.
1325 coreconfigitem('worker', 'backgroundclosemaxqueue',
1328 coreconfigitem('worker', 'backgroundclosemaxqueue',
1326 default=384,
1329 default=384,
1327 )
1330 )
1328 coreconfigitem('worker', 'backgroundcloseminfilecount',
1331 coreconfigitem('worker', 'backgroundcloseminfilecount',
1329 default=2048,
1332 default=2048,
1330 )
1333 )
1331 coreconfigitem('worker', 'backgroundclosethreadcount',
1334 coreconfigitem('worker', 'backgroundclosethreadcount',
1332 default=4,
1335 default=4,
1333 )
1336 )
1334 coreconfigitem('worker', 'enabled',
1337 coreconfigitem('worker', 'enabled',
1335 default=True,
1338 default=True,
1336 )
1339 )
1337 coreconfigitem('worker', 'numcpus',
1340 coreconfigitem('worker', 'numcpus',
1338 default=None,
1341 default=None,
1339 )
1342 )
1340
1343
1341 # Rebase related configuration moved to core because other extension are doing
1344 # Rebase related configuration moved to core because other extension are doing
1342 # strange things. For example, shelve import the extensions to reuse some bit
1345 # strange things. For example, shelve import the extensions to reuse some bit
1343 # without formally loading it.
1346 # without formally loading it.
1344 coreconfigitem('commands', 'rebase.requiredest',
1347 coreconfigitem('commands', 'rebase.requiredest',
1345 default=False,
1348 default=False,
1346 )
1349 )
1347 coreconfigitem('experimental', 'rebaseskipobsolete',
1350 coreconfigitem('experimental', 'rebaseskipobsolete',
1348 default=True,
1351 default=True,
1349 )
1352 )
1350 coreconfigitem('rebase', 'singletransaction',
1353 coreconfigitem('rebase', 'singletransaction',
1351 default=False,
1354 default=False,
1352 )
1355 )
1353 coreconfigitem('rebase', 'experimental.inmemory',
1356 coreconfigitem('rebase', 'experimental.inmemory',
1354 default=False,
1357 default=False,
1355 )
1358 )
@@ -1,1873 +1,1905 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 traceback
21 import traceback
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import hex
24 from .node import hex
25
25
26 from . import (
26 from . import (
27 color,
27 color,
28 config,
28 config,
29 configitems,
29 configitems,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 progress,
33 progress,
34 pycompat,
34 pycompat,
35 rcutil,
35 rcutil,
36 scmutil,
36 scmutil,
37 util,
37 util,
38 )
38 )
39 from .utils import (
39 from .utils import (
40 dateutil,
40 dateutil,
41 procutil,
41 procutil,
42 stringutil,
42 stringutil,
43 )
43 )
44
44
45 urlreq = util.urlreq
45 urlreq = util.urlreq
46
46
47 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
47 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
48 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
48 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
49 if not c.isalnum())
49 if not c.isalnum())
50
50
51 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
51 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
52 tweakrc = b"""
52 tweakrc = b"""
53 [ui]
53 [ui]
54 # The rollback command is dangerous. As a rule, don't use it.
54 # The rollback command is dangerous. As a rule, don't use it.
55 rollback = False
55 rollback = False
56 # Make `hg status` report copy information
56 # Make `hg status` report copy information
57 statuscopies = yes
57 statuscopies = yes
58 # Prefer curses UIs when available. Revert to plain-text with `text`.
58 # Prefer curses UIs when available. Revert to plain-text with `text`.
59 interface = curses
59 interface = curses
60
60
61 [commands]
61 [commands]
62 # Make `hg status` emit cwd-relative paths by default.
62 # Make `hg status` emit cwd-relative paths by default.
63 status.relative = yes
63 status.relative = yes
64 # Refuse to perform an `hg update` that would cause a file content merge
64 # Refuse to perform an `hg update` that would cause a file content merge
65 update.check = noconflict
65 update.check = noconflict
66 # Show conflicts information in `hg status`
66 # Show conflicts information in `hg status`
67 status.verbose = True
67 status.verbose = True
68 # Collapse entire directories that contain only unknown files
68 # Collapse entire directories that contain only unknown files
69 status.terse = u
69 status.terse = u
70
70
71 [diff]
71 [diff]
72 git = 1
72 git = 1
73 showfunc = 1
73 showfunc = 1
74 """
74 """
75
75
76 samplehgrcs = {
76 samplehgrcs = {
77 'user':
77 'user':
78 b"""# example user config (see 'hg help config' for more info)
78 b"""# example user config (see 'hg help config' for more info)
79 [ui]
79 [ui]
80 # name and email, e.g.
80 # name and email, e.g.
81 # username = Jane Doe <jdoe@example.com>
81 # username = Jane Doe <jdoe@example.com>
82 username =
82 username =
83
83
84 # We recommend enabling tweakdefaults to get slight improvements to
84 # We recommend enabling tweakdefaults to get slight improvements to
85 # the UI over time. Make sure to set HGPLAIN in the environment when
85 # the UI over time. Make sure to set HGPLAIN in the environment when
86 # writing scripts!
86 # writing scripts!
87 # tweakdefaults = True
87 # tweakdefaults = True
88
88
89 # uncomment to disable color in command output
89 # uncomment to disable color in command output
90 # (see 'hg help color' for details)
90 # (see 'hg help color' for details)
91 # color = never
91 # color = never
92
92
93 # uncomment to disable command output pagination
93 # uncomment to disable command output pagination
94 # (see 'hg help pager' for details)
94 # (see 'hg help pager' for details)
95 # paginate = never
95 # paginate = never
96
96
97 [extensions]
97 [extensions]
98 # uncomment these lines to enable some popular extensions
98 # uncomment these lines to enable some popular extensions
99 # (see 'hg help extensions' for more info)
99 # (see 'hg help extensions' for more info)
100 #
100 #
101 # churn =
101 # churn =
102 """,
102 """,
103
103
104 'cloned':
104 'cloned':
105 b"""# example repository config (see 'hg help config' for more info)
105 b"""# example repository config (see 'hg help config' for more info)
106 [paths]
106 [paths]
107 default = %s
107 default = %s
108
108
109 # path aliases to other clones of this repo in URLs or filesystem paths
109 # path aliases to other clones of this repo in URLs or filesystem paths
110 # (see 'hg help config.paths' for more info)
110 # (see 'hg help config.paths' for more info)
111 #
111 #
112 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
112 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
113 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
113 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
114 # my-clone = /home/jdoe/jdoes-clone
114 # my-clone = /home/jdoe/jdoes-clone
115
115
116 [ui]
116 [ui]
117 # name and email (local to this repository, optional), e.g.
117 # name and email (local to this repository, optional), e.g.
118 # username = Jane Doe <jdoe@example.com>
118 # username = Jane Doe <jdoe@example.com>
119 """,
119 """,
120
120
121 'local':
121 'local':
122 b"""# example repository config (see 'hg help config' for more info)
122 b"""# example repository config (see 'hg help config' for more info)
123 [paths]
123 [paths]
124 # path aliases to other clones of this repo in URLs or filesystem paths
124 # path aliases to other clones of this repo in URLs or filesystem paths
125 # (see 'hg help config.paths' for more info)
125 # (see 'hg help config.paths' for more info)
126 #
126 #
127 # default = http://example.com/hg/example-repo
127 # default = http://example.com/hg/example-repo
128 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
128 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
129 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
129 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
130 # my-clone = /home/jdoe/jdoes-clone
130 # my-clone = /home/jdoe/jdoes-clone
131
131
132 [ui]
132 [ui]
133 # name and email (local to this repository, optional), e.g.
133 # name and email (local to this repository, optional), e.g.
134 # username = Jane Doe <jdoe@example.com>
134 # username = Jane Doe <jdoe@example.com>
135 """,
135 """,
136
136
137 'global':
137 'global':
138 b"""# example system-wide hg config (see 'hg help config' for more info)
138 b"""# example system-wide hg config (see 'hg help config' for more info)
139
139
140 [ui]
140 [ui]
141 # uncomment to disable color in command output
141 # uncomment to disable color in command output
142 # (see 'hg help color' for details)
142 # (see 'hg help color' for details)
143 # color = never
143 # color = never
144
144
145 # uncomment to disable command output pagination
145 # uncomment to disable command output pagination
146 # (see 'hg help pager' for details)
146 # (see 'hg help pager' for details)
147 # paginate = never
147 # paginate = never
148
148
149 [extensions]
149 [extensions]
150 # uncomment these lines to enable some popular extensions
150 # uncomment these lines to enable some popular extensions
151 # (see 'hg help extensions' for more info)
151 # (see 'hg help extensions' for more info)
152 #
152 #
153 # blackbox =
153 # blackbox =
154 # churn =
154 # churn =
155 """,
155 """,
156 }
156 }
157
157
158 def _maybestrurl(maybebytes):
158 def _maybestrurl(maybebytes):
159 return util.rapply(pycompat.strurl, maybebytes)
159 return util.rapply(pycompat.strurl, maybebytes)
160
160
161 def _maybebytesurl(maybestr):
161 def _maybebytesurl(maybestr):
162 return util.rapply(pycompat.bytesurl, maybestr)
162 return util.rapply(pycompat.bytesurl, maybestr)
163
163
164 class httppasswordmgrdbproxy(object):
164 class httppasswordmgrdbproxy(object):
165 """Delays loading urllib2 until it's needed."""
165 """Delays loading urllib2 until it's needed."""
166 def __init__(self):
166 def __init__(self):
167 self._mgr = None
167 self._mgr = None
168
168
169 def _get_mgr(self):
169 def _get_mgr(self):
170 if self._mgr is None:
170 if self._mgr is None:
171 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
171 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
172 return self._mgr
172 return self._mgr
173
173
174 def add_password(self, realm, uris, user, passwd):
174 def add_password(self, realm, uris, user, passwd):
175 return self._get_mgr().add_password(
175 return self._get_mgr().add_password(
176 _maybestrurl(realm), _maybestrurl(uris),
176 _maybestrurl(realm), _maybestrurl(uris),
177 _maybestrurl(user), _maybestrurl(passwd))
177 _maybestrurl(user), _maybestrurl(passwd))
178
178
179 def find_user_password(self, realm, uri):
179 def find_user_password(self, realm, uri):
180 mgr = self._get_mgr()
180 mgr = self._get_mgr()
181 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
181 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
182 _maybestrurl(uri)))
182 _maybestrurl(uri)))
183
183
184 def _catchterm(*args):
184 def _catchterm(*args):
185 raise error.SignalInterrupt
185 raise error.SignalInterrupt
186
186
187 # unique object used to detect no default value has been provided when
187 # unique object used to detect no default value has been provided when
188 # retrieving configuration value.
188 # retrieving configuration value.
189 _unset = object()
189 _unset = object()
190
190
191 # _reqexithandlers: callbacks run at the end of a request
191 # _reqexithandlers: callbacks run at the end of a request
192 _reqexithandlers = []
192 _reqexithandlers = []
193
193
194 class ui(object):
194 class ui(object):
195 def __init__(self, src=None):
195 def __init__(self, src=None):
196 """Create a fresh new ui object if no src given
196 """Create a fresh new ui object if no src given
197
197
198 Use uimod.ui.load() to create a ui which knows global and user configs.
198 Use uimod.ui.load() to create a ui which knows global and user configs.
199 In most cases, you should use ui.copy() to create a copy of an existing
199 In most cases, you should use ui.copy() to create a copy of an existing
200 ui object.
200 ui object.
201 """
201 """
202 # _buffers: used for temporary capture of output
202 # _buffers: used for temporary capture of output
203 self._buffers = []
203 self._buffers = []
204 # 3-tuple describing how each buffer in the stack behaves.
204 # 3-tuple describing how each buffer in the stack behaves.
205 # Values are (capture stderr, capture subprocesses, apply labels).
205 # Values are (capture stderr, capture subprocesses, apply labels).
206 self._bufferstates = []
206 self._bufferstates = []
207 # When a buffer is active, defines whether we are expanding labels.
207 # When a buffer is active, defines whether we are expanding labels.
208 # This exists to prevent an extra list lookup.
208 # This exists to prevent an extra list lookup.
209 self._bufferapplylabels = None
209 self._bufferapplylabels = None
210 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
210 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
211 self._reportuntrusted = True
211 self._reportuntrusted = True
212 self._knownconfig = configitems.coreitems
212 self._knownconfig = configitems.coreitems
213 self._ocfg = config.config() # overlay
213 self._ocfg = config.config() # overlay
214 self._tcfg = config.config() # trusted
214 self._tcfg = config.config() # trusted
215 self._ucfg = config.config() # untrusted
215 self._ucfg = config.config() # untrusted
216 self._trustusers = set()
216 self._trustusers = set()
217 self._trustgroups = set()
217 self._trustgroups = set()
218 self.callhooks = True
218 self.callhooks = True
219 # Insecure server connections requested.
219 # Insecure server connections requested.
220 self.insecureconnections = False
220 self.insecureconnections = False
221 # Blocked time
221 # Blocked time
222 self.logblockedtimes = False
222 self.logblockedtimes = False
223 # color mode: see mercurial/color.py for possible value
223 # color mode: see mercurial/color.py for possible value
224 self._colormode = None
224 self._colormode = None
225 self._terminfoparams = {}
225 self._terminfoparams = {}
226 self._styles = {}
226 self._styles = {}
227 self._uninterruptible = False
227
228
228 if src:
229 if src:
229 self.fout = src.fout
230 self.fout = src.fout
230 self.ferr = src.ferr
231 self.ferr = src.ferr
231 self.fin = src.fin
232 self.fin = src.fin
232 self.pageractive = src.pageractive
233 self.pageractive = src.pageractive
233 self._disablepager = src._disablepager
234 self._disablepager = src._disablepager
234 self._tweaked = src._tweaked
235 self._tweaked = src._tweaked
235
236
236 self._tcfg = src._tcfg.copy()
237 self._tcfg = src._tcfg.copy()
237 self._ucfg = src._ucfg.copy()
238 self._ucfg = src._ucfg.copy()
238 self._ocfg = src._ocfg.copy()
239 self._ocfg = src._ocfg.copy()
239 self._trustusers = src._trustusers.copy()
240 self._trustusers = src._trustusers.copy()
240 self._trustgroups = src._trustgroups.copy()
241 self._trustgroups = src._trustgroups.copy()
241 self.environ = src.environ
242 self.environ = src.environ
242 self.callhooks = src.callhooks
243 self.callhooks = src.callhooks
243 self.insecureconnections = src.insecureconnections
244 self.insecureconnections = src.insecureconnections
244 self._colormode = src._colormode
245 self._colormode = src._colormode
245 self._terminfoparams = src._terminfoparams.copy()
246 self._terminfoparams = src._terminfoparams.copy()
246 self._styles = src._styles.copy()
247 self._styles = src._styles.copy()
247
248
248 self.fixconfig()
249 self.fixconfig()
249
250
250 self.httppasswordmgrdb = src.httppasswordmgrdb
251 self.httppasswordmgrdb = src.httppasswordmgrdb
251 self._blockedtimes = src._blockedtimes
252 self._blockedtimes = src._blockedtimes
252 else:
253 else:
253 self.fout = procutil.stdout
254 self.fout = procutil.stdout
254 self.ferr = procutil.stderr
255 self.ferr = procutil.stderr
255 self.fin = procutil.stdin
256 self.fin = procutil.stdin
256 self.pageractive = False
257 self.pageractive = False
257 self._disablepager = False
258 self._disablepager = False
258 self._tweaked = False
259 self._tweaked = False
259
260
260 # shared read-only environment
261 # shared read-only environment
261 self.environ = encoding.environ
262 self.environ = encoding.environ
262
263
263 self.httppasswordmgrdb = httppasswordmgrdbproxy()
264 self.httppasswordmgrdb = httppasswordmgrdbproxy()
264 self._blockedtimes = collections.defaultdict(int)
265 self._blockedtimes = collections.defaultdict(int)
265
266
266 allowed = self.configlist('experimental', 'exportableenviron')
267 allowed = self.configlist('experimental', 'exportableenviron')
267 if '*' in allowed:
268 if '*' in allowed:
268 self._exportableenviron = self.environ
269 self._exportableenviron = self.environ
269 else:
270 else:
270 self._exportableenviron = {}
271 self._exportableenviron = {}
271 for k in allowed:
272 for k in allowed:
272 if k in self.environ:
273 if k in self.environ:
273 self._exportableenviron[k] = self.environ[k]
274 self._exportableenviron[k] = self.environ[k]
274
275
275 @classmethod
276 @classmethod
276 def load(cls):
277 def load(cls):
277 """Create a ui and load global and user configs"""
278 """Create a ui and load global and user configs"""
278 u = cls()
279 u = cls()
279 # we always trust global config files and environment variables
280 # we always trust global config files and environment variables
280 for t, f in rcutil.rccomponents():
281 for t, f in rcutil.rccomponents():
281 if t == 'path':
282 if t == 'path':
282 u.readconfig(f, trust=True)
283 u.readconfig(f, trust=True)
283 elif t == 'items':
284 elif t == 'items':
284 sections = set()
285 sections = set()
285 for section, name, value, source in f:
286 for section, name, value, source in f:
286 # do not set u._ocfg
287 # do not set u._ocfg
287 # XXX clean this up once immutable config object is a thing
288 # XXX clean this up once immutable config object is a thing
288 u._tcfg.set(section, name, value, source)
289 u._tcfg.set(section, name, value, source)
289 u._ucfg.set(section, name, value, source)
290 u._ucfg.set(section, name, value, source)
290 sections.add(section)
291 sections.add(section)
291 for section in sections:
292 for section in sections:
292 u.fixconfig(section=section)
293 u.fixconfig(section=section)
293 else:
294 else:
294 raise error.ProgrammingError('unknown rctype: %s' % t)
295 raise error.ProgrammingError('unknown rctype: %s' % t)
295 u._maybetweakdefaults()
296 u._maybetweakdefaults()
296 return u
297 return u
297
298
298 def _maybetweakdefaults(self):
299 def _maybetweakdefaults(self):
299 if not self.configbool('ui', 'tweakdefaults'):
300 if not self.configbool('ui', 'tweakdefaults'):
300 return
301 return
301 if self._tweaked or self.plain('tweakdefaults'):
302 if self._tweaked or self.plain('tweakdefaults'):
302 return
303 return
303
304
304 # Note: it is SUPER IMPORTANT that you set self._tweaked to
305 # Note: it is SUPER IMPORTANT that you set self._tweaked to
305 # True *before* any calls to setconfig(), otherwise you'll get
306 # True *before* any calls to setconfig(), otherwise you'll get
306 # infinite recursion between setconfig and this method.
307 # infinite recursion between setconfig and this method.
307 #
308 #
308 # TODO: We should extract an inner method in setconfig() to
309 # TODO: We should extract an inner method in setconfig() to
309 # avoid this weirdness.
310 # avoid this weirdness.
310 self._tweaked = True
311 self._tweaked = True
311 tmpcfg = config.config()
312 tmpcfg = config.config()
312 tmpcfg.parse('<tweakdefaults>', tweakrc)
313 tmpcfg.parse('<tweakdefaults>', tweakrc)
313 for section in tmpcfg:
314 for section in tmpcfg:
314 for name, value in tmpcfg.items(section):
315 for name, value in tmpcfg.items(section):
315 if not self.hasconfig(section, name):
316 if not self.hasconfig(section, name):
316 self.setconfig(section, name, value, "<tweakdefaults>")
317 self.setconfig(section, name, value, "<tweakdefaults>")
317
318
318 def copy(self):
319 def copy(self):
319 return self.__class__(self)
320 return self.__class__(self)
320
321
321 def resetstate(self):
322 def resetstate(self):
322 """Clear internal state that shouldn't persist across commands"""
323 """Clear internal state that shouldn't persist across commands"""
323 if self._progbar:
324 if self._progbar:
324 self._progbar.resetstate() # reset last-print time of progress bar
325 self._progbar.resetstate() # reset last-print time of progress bar
325 self.httppasswordmgrdb = httppasswordmgrdbproxy()
326 self.httppasswordmgrdb = httppasswordmgrdbproxy()
326
327
327 @contextlib.contextmanager
328 @contextlib.contextmanager
328 def timeblockedsection(self, key):
329 def timeblockedsection(self, key):
329 # this is open-coded below - search for timeblockedsection to find them
330 # this is open-coded below - search for timeblockedsection to find them
330 starttime = util.timer()
331 starttime = util.timer()
331 try:
332 try:
332 yield
333 yield
333 finally:
334 finally:
334 self._blockedtimes[key + '_blocked'] += \
335 self._blockedtimes[key + '_blocked'] += \
335 (util.timer() - starttime) * 1000
336 (util.timer() - starttime) * 1000
336
337
338 @contextlib.contextmanager
339 def uninterruptable(self):
340 """Mark an operation as unsafe.
341
342 Most operations on a repository are safe to interrupt, but a
343 few are risky (for example repair.strip). This context manager
344 lets you advise Mercurial that something risky is happening so
345 that control-C etc can be blocked if desired.
346 """
347 enabled = self.configbool('experimental', 'nointerrupt')
348 if (enabled and
349 self.configbool('experimental', 'nointerrupt-interactiveonly')):
350 enabled = self.interactive()
351 if self._uninterruptible or not enabled:
352 # if nointerrupt support is turned off, the process isn't
353 # interactive, or we're already in an uninterruptable
354 # block, do nothing.
355 yield
356 return
357 def warn():
358 self.warn(_("shutting down cleanly\n"))
359 self.warn(
360 _("press ^C again to terminate immediately (dangerous)\n"))
361 return True
362 with procutil.uninterruptable(warn):
363 try:
364 self._uninterruptible = True
365 yield
366 finally:
367 self._uninterruptible = False
368
337 def formatter(self, topic, opts):
369 def formatter(self, topic, opts):
338 return formatter.formatter(self, self, topic, opts)
370 return formatter.formatter(self, self, topic, opts)
339
371
340 def _trusted(self, fp, f):
372 def _trusted(self, fp, f):
341 st = util.fstat(fp)
373 st = util.fstat(fp)
342 if util.isowner(st):
374 if util.isowner(st):
343 return True
375 return True
344
376
345 tusers, tgroups = self._trustusers, self._trustgroups
377 tusers, tgroups = self._trustusers, self._trustgroups
346 if '*' in tusers or '*' in tgroups:
378 if '*' in tusers or '*' in tgroups:
347 return True
379 return True
348
380
349 user = util.username(st.st_uid)
381 user = util.username(st.st_uid)
350 group = util.groupname(st.st_gid)
382 group = util.groupname(st.st_gid)
351 if user in tusers or group in tgroups or user == util.username():
383 if user in tusers or group in tgroups or user == util.username():
352 return True
384 return True
353
385
354 if self._reportuntrusted:
386 if self._reportuntrusted:
355 self.warn(_('not trusting file %s from untrusted '
387 self.warn(_('not trusting file %s from untrusted '
356 'user %s, group %s\n') % (f, user, group))
388 'user %s, group %s\n') % (f, user, group))
357 return False
389 return False
358
390
359 def readconfig(self, filename, root=None, trust=False,
391 def readconfig(self, filename, root=None, trust=False,
360 sections=None, remap=None):
392 sections=None, remap=None):
361 try:
393 try:
362 fp = open(filename, u'rb')
394 fp = open(filename, u'rb')
363 except IOError:
395 except IOError:
364 if not sections: # ignore unless we were looking for something
396 if not sections: # ignore unless we were looking for something
365 return
397 return
366 raise
398 raise
367
399
368 cfg = config.config()
400 cfg = config.config()
369 trusted = sections or trust or self._trusted(fp, filename)
401 trusted = sections or trust or self._trusted(fp, filename)
370
402
371 try:
403 try:
372 cfg.read(filename, fp, sections=sections, remap=remap)
404 cfg.read(filename, fp, sections=sections, remap=remap)
373 fp.close()
405 fp.close()
374 except error.ConfigError as inst:
406 except error.ConfigError as inst:
375 if trusted:
407 if trusted:
376 raise
408 raise
377 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
409 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst))
378
410
379 if self.plain():
411 if self.plain():
380 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
412 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
381 'logtemplate', 'statuscopies', 'style',
413 'logtemplate', 'statuscopies', 'style',
382 'traceback', 'verbose'):
414 'traceback', 'verbose'):
383 if k in cfg['ui']:
415 if k in cfg['ui']:
384 del cfg['ui'][k]
416 del cfg['ui'][k]
385 for k, v in cfg.items('defaults'):
417 for k, v in cfg.items('defaults'):
386 del cfg['defaults'][k]
418 del cfg['defaults'][k]
387 for k, v in cfg.items('commands'):
419 for k, v in cfg.items('commands'):
388 del cfg['commands'][k]
420 del cfg['commands'][k]
389 # Don't remove aliases from the configuration if in the exceptionlist
421 # Don't remove aliases from the configuration if in the exceptionlist
390 if self.plain('alias'):
422 if self.plain('alias'):
391 for k, v in cfg.items('alias'):
423 for k, v in cfg.items('alias'):
392 del cfg['alias'][k]
424 del cfg['alias'][k]
393 if self.plain('revsetalias'):
425 if self.plain('revsetalias'):
394 for k, v in cfg.items('revsetalias'):
426 for k, v in cfg.items('revsetalias'):
395 del cfg['revsetalias'][k]
427 del cfg['revsetalias'][k]
396 if self.plain('templatealias'):
428 if self.plain('templatealias'):
397 for k, v in cfg.items('templatealias'):
429 for k, v in cfg.items('templatealias'):
398 del cfg['templatealias'][k]
430 del cfg['templatealias'][k]
399
431
400 if trusted:
432 if trusted:
401 self._tcfg.update(cfg)
433 self._tcfg.update(cfg)
402 self._tcfg.update(self._ocfg)
434 self._tcfg.update(self._ocfg)
403 self._ucfg.update(cfg)
435 self._ucfg.update(cfg)
404 self._ucfg.update(self._ocfg)
436 self._ucfg.update(self._ocfg)
405
437
406 if root is None:
438 if root is None:
407 root = os.path.expanduser('~')
439 root = os.path.expanduser('~')
408 self.fixconfig(root=root)
440 self.fixconfig(root=root)
409
441
410 def fixconfig(self, root=None, section=None):
442 def fixconfig(self, root=None, section=None):
411 if section in (None, 'paths'):
443 if section in (None, 'paths'):
412 # expand vars and ~
444 # expand vars and ~
413 # translate paths relative to root (or home) into absolute paths
445 # translate paths relative to root (or home) into absolute paths
414 root = root or pycompat.getcwd()
446 root = root or pycompat.getcwd()
415 for c in self._tcfg, self._ucfg, self._ocfg:
447 for c in self._tcfg, self._ucfg, self._ocfg:
416 for n, p in c.items('paths'):
448 for n, p in c.items('paths'):
417 # Ignore sub-options.
449 # Ignore sub-options.
418 if ':' in n:
450 if ':' in n:
419 continue
451 continue
420 if not p:
452 if not p:
421 continue
453 continue
422 if '%%' in p:
454 if '%%' in p:
423 s = self.configsource('paths', n) or 'none'
455 s = self.configsource('paths', n) or 'none'
424 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
456 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
425 % (n, p, s))
457 % (n, p, s))
426 p = p.replace('%%', '%')
458 p = p.replace('%%', '%')
427 p = util.expandpath(p)
459 p = util.expandpath(p)
428 if not util.hasscheme(p) and not os.path.isabs(p):
460 if not util.hasscheme(p) and not os.path.isabs(p):
429 p = os.path.normpath(os.path.join(root, p))
461 p = os.path.normpath(os.path.join(root, p))
430 c.set("paths", n, p)
462 c.set("paths", n, p)
431
463
432 if section in (None, 'ui'):
464 if section in (None, 'ui'):
433 # update ui options
465 # update ui options
434 self.debugflag = self.configbool('ui', 'debug')
466 self.debugflag = self.configbool('ui', 'debug')
435 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
467 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
436 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
468 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
437 if self.verbose and self.quiet:
469 if self.verbose and self.quiet:
438 self.quiet = self.verbose = False
470 self.quiet = self.verbose = False
439 self._reportuntrusted = self.debugflag or self.configbool("ui",
471 self._reportuntrusted = self.debugflag or self.configbool("ui",
440 "report_untrusted")
472 "report_untrusted")
441 self.tracebackflag = self.configbool('ui', 'traceback')
473 self.tracebackflag = self.configbool('ui', 'traceback')
442 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
474 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
443
475
444 if section in (None, 'trusted'):
476 if section in (None, 'trusted'):
445 # update trust information
477 # update trust information
446 self._trustusers.update(self.configlist('trusted', 'users'))
478 self._trustusers.update(self.configlist('trusted', 'users'))
447 self._trustgroups.update(self.configlist('trusted', 'groups'))
479 self._trustgroups.update(self.configlist('trusted', 'groups'))
448
480
449 def backupconfig(self, section, item):
481 def backupconfig(self, section, item):
450 return (self._ocfg.backup(section, item),
482 return (self._ocfg.backup(section, item),
451 self._tcfg.backup(section, item),
483 self._tcfg.backup(section, item),
452 self._ucfg.backup(section, item),)
484 self._ucfg.backup(section, item),)
453 def restoreconfig(self, data):
485 def restoreconfig(self, data):
454 self._ocfg.restore(data[0])
486 self._ocfg.restore(data[0])
455 self._tcfg.restore(data[1])
487 self._tcfg.restore(data[1])
456 self._ucfg.restore(data[2])
488 self._ucfg.restore(data[2])
457
489
458 def setconfig(self, section, name, value, source=''):
490 def setconfig(self, section, name, value, source=''):
459 for cfg in (self._ocfg, self._tcfg, self._ucfg):
491 for cfg in (self._ocfg, self._tcfg, self._ucfg):
460 cfg.set(section, name, value, source)
492 cfg.set(section, name, value, source)
461 self.fixconfig(section=section)
493 self.fixconfig(section=section)
462 self._maybetweakdefaults()
494 self._maybetweakdefaults()
463
495
464 def _data(self, untrusted):
496 def _data(self, untrusted):
465 return untrusted and self._ucfg or self._tcfg
497 return untrusted and self._ucfg or self._tcfg
466
498
467 def configsource(self, section, name, untrusted=False):
499 def configsource(self, section, name, untrusted=False):
468 return self._data(untrusted).source(section, name)
500 return self._data(untrusted).source(section, name)
469
501
470 def config(self, section, name, default=_unset, untrusted=False):
502 def config(self, section, name, default=_unset, untrusted=False):
471 """return the plain string version of a config"""
503 """return the plain string version of a config"""
472 value = self._config(section, name, default=default,
504 value = self._config(section, name, default=default,
473 untrusted=untrusted)
505 untrusted=untrusted)
474 if value is _unset:
506 if value is _unset:
475 return None
507 return None
476 return value
508 return value
477
509
478 def _config(self, section, name, default=_unset, untrusted=False):
510 def _config(self, section, name, default=_unset, untrusted=False):
479 value = itemdefault = default
511 value = itemdefault = default
480 item = self._knownconfig.get(section, {}).get(name)
512 item = self._knownconfig.get(section, {}).get(name)
481 alternates = [(section, name)]
513 alternates = [(section, name)]
482
514
483 if item is not None:
515 if item is not None:
484 alternates.extend(item.alias)
516 alternates.extend(item.alias)
485 if callable(item.default):
517 if callable(item.default):
486 itemdefault = item.default()
518 itemdefault = item.default()
487 else:
519 else:
488 itemdefault = item.default
520 itemdefault = item.default
489 else:
521 else:
490 msg = ("accessing unregistered config item: '%s.%s'")
522 msg = ("accessing unregistered config item: '%s.%s'")
491 msg %= (section, name)
523 msg %= (section, name)
492 self.develwarn(msg, 2, 'warn-config-unknown')
524 self.develwarn(msg, 2, 'warn-config-unknown')
493
525
494 if default is _unset:
526 if default is _unset:
495 if item is None:
527 if item is None:
496 value = default
528 value = default
497 elif item.default is configitems.dynamicdefault:
529 elif item.default is configitems.dynamicdefault:
498 value = None
530 value = None
499 msg = "config item requires an explicit default value: '%s.%s'"
531 msg = "config item requires an explicit default value: '%s.%s'"
500 msg %= (section, name)
532 msg %= (section, name)
501 self.develwarn(msg, 2, 'warn-config-default')
533 self.develwarn(msg, 2, 'warn-config-default')
502 else:
534 else:
503 value = itemdefault
535 value = itemdefault
504 elif (item is not None
536 elif (item is not None
505 and item.default is not configitems.dynamicdefault
537 and item.default is not configitems.dynamicdefault
506 and default != itemdefault):
538 and default != itemdefault):
507 msg = ("specifying a mismatched default value for a registered "
539 msg = ("specifying a mismatched default value for a registered "
508 "config item: '%s.%s' '%s'")
540 "config item: '%s.%s' '%s'")
509 msg %= (section, name, pycompat.bytestr(default))
541 msg %= (section, name, pycompat.bytestr(default))
510 self.develwarn(msg, 2, 'warn-config-default')
542 self.develwarn(msg, 2, 'warn-config-default')
511
543
512 for s, n in alternates:
544 for s, n in alternates:
513 candidate = self._data(untrusted).get(s, n, None)
545 candidate = self._data(untrusted).get(s, n, None)
514 if candidate is not None:
546 if candidate is not None:
515 value = candidate
547 value = candidate
516 section = s
548 section = s
517 name = n
549 name = n
518 break
550 break
519
551
520 if self.debugflag and not untrusted and self._reportuntrusted:
552 if self.debugflag and not untrusted and self._reportuntrusted:
521 for s, n in alternates:
553 for s, n in alternates:
522 uvalue = self._ucfg.get(s, n)
554 uvalue = self._ucfg.get(s, n)
523 if uvalue is not None and uvalue != value:
555 if uvalue is not None and uvalue != value:
524 self.debug("ignoring untrusted configuration option "
556 self.debug("ignoring untrusted configuration option "
525 "%s.%s = %s\n" % (s, n, uvalue))
557 "%s.%s = %s\n" % (s, n, uvalue))
526 return value
558 return value
527
559
528 def configsuboptions(self, section, name, default=_unset, untrusted=False):
560 def configsuboptions(self, section, name, default=_unset, untrusted=False):
529 """Get a config option and all sub-options.
561 """Get a config option and all sub-options.
530
562
531 Some config options have sub-options that are declared with the
563 Some config options have sub-options that are declared with the
532 format "key:opt = value". This method is used to return the main
564 format "key:opt = value". This method is used to return the main
533 option and all its declared sub-options.
565 option and all its declared sub-options.
534
566
535 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
567 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
536 is a dict of defined sub-options where keys and values are strings.
568 is a dict of defined sub-options where keys and values are strings.
537 """
569 """
538 main = self.config(section, name, default, untrusted=untrusted)
570 main = self.config(section, name, default, untrusted=untrusted)
539 data = self._data(untrusted)
571 data = self._data(untrusted)
540 sub = {}
572 sub = {}
541 prefix = '%s:' % name
573 prefix = '%s:' % name
542 for k, v in data.items(section):
574 for k, v in data.items(section):
543 if k.startswith(prefix):
575 if k.startswith(prefix):
544 sub[k[len(prefix):]] = v
576 sub[k[len(prefix):]] = v
545
577
546 if self.debugflag and not untrusted and self._reportuntrusted:
578 if self.debugflag and not untrusted and self._reportuntrusted:
547 for k, v in sub.items():
579 for k, v in sub.items():
548 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
580 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
549 if uvalue is not None and uvalue != v:
581 if uvalue is not None and uvalue != v:
550 self.debug('ignoring untrusted configuration option '
582 self.debug('ignoring untrusted configuration option '
551 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
583 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
552
584
553 return main, sub
585 return main, sub
554
586
555 def configpath(self, section, name, default=_unset, untrusted=False):
587 def configpath(self, section, name, default=_unset, untrusted=False):
556 'get a path config item, expanded relative to repo root or config file'
588 'get a path config item, expanded relative to repo root or config file'
557 v = self.config(section, name, default, untrusted)
589 v = self.config(section, name, default, untrusted)
558 if v is None:
590 if v is None:
559 return None
591 return None
560 if not os.path.isabs(v) or "://" not in v:
592 if not os.path.isabs(v) or "://" not in v:
561 src = self.configsource(section, name, untrusted)
593 src = self.configsource(section, name, untrusted)
562 if ':' in src:
594 if ':' in src:
563 base = os.path.dirname(src.rsplit(':')[0])
595 base = os.path.dirname(src.rsplit(':')[0])
564 v = os.path.join(base, os.path.expanduser(v))
596 v = os.path.join(base, os.path.expanduser(v))
565 return v
597 return v
566
598
567 def configbool(self, section, name, default=_unset, untrusted=False):
599 def configbool(self, section, name, default=_unset, untrusted=False):
568 """parse a configuration element as a boolean
600 """parse a configuration element as a boolean
569
601
570 >>> u = ui(); s = b'foo'
602 >>> u = ui(); s = b'foo'
571 >>> u.setconfig(s, b'true', b'yes')
603 >>> u.setconfig(s, b'true', b'yes')
572 >>> u.configbool(s, b'true')
604 >>> u.configbool(s, b'true')
573 True
605 True
574 >>> u.setconfig(s, b'false', b'no')
606 >>> u.setconfig(s, b'false', b'no')
575 >>> u.configbool(s, b'false')
607 >>> u.configbool(s, b'false')
576 False
608 False
577 >>> u.configbool(s, b'unknown')
609 >>> u.configbool(s, b'unknown')
578 False
610 False
579 >>> u.configbool(s, b'unknown', True)
611 >>> u.configbool(s, b'unknown', True)
580 True
612 True
581 >>> u.setconfig(s, b'invalid', b'somevalue')
613 >>> u.setconfig(s, b'invalid', b'somevalue')
582 >>> u.configbool(s, b'invalid')
614 >>> u.configbool(s, b'invalid')
583 Traceback (most recent call last):
615 Traceback (most recent call last):
584 ...
616 ...
585 ConfigError: foo.invalid is not a boolean ('somevalue')
617 ConfigError: foo.invalid is not a boolean ('somevalue')
586 """
618 """
587
619
588 v = self._config(section, name, default, untrusted=untrusted)
620 v = self._config(section, name, default, untrusted=untrusted)
589 if v is None:
621 if v is None:
590 return v
622 return v
591 if v is _unset:
623 if v is _unset:
592 if default is _unset:
624 if default is _unset:
593 return False
625 return False
594 return default
626 return default
595 if isinstance(v, bool):
627 if isinstance(v, bool):
596 return v
628 return v
597 b = stringutil.parsebool(v)
629 b = stringutil.parsebool(v)
598 if b is None:
630 if b is None:
599 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
631 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
600 % (section, name, v))
632 % (section, name, v))
601 return b
633 return b
602
634
603 def configwith(self, convert, section, name, default=_unset,
635 def configwith(self, convert, section, name, default=_unset,
604 desc=None, untrusted=False):
636 desc=None, untrusted=False):
605 """parse a configuration element with a conversion function
637 """parse a configuration element with a conversion function
606
638
607 >>> u = ui(); s = b'foo'
639 >>> u = ui(); s = b'foo'
608 >>> u.setconfig(s, b'float1', b'42')
640 >>> u.setconfig(s, b'float1', b'42')
609 >>> u.configwith(float, s, b'float1')
641 >>> u.configwith(float, s, b'float1')
610 42.0
642 42.0
611 >>> u.setconfig(s, b'float2', b'-4.25')
643 >>> u.setconfig(s, b'float2', b'-4.25')
612 >>> u.configwith(float, s, b'float2')
644 >>> u.configwith(float, s, b'float2')
613 -4.25
645 -4.25
614 >>> u.configwith(float, s, b'unknown', 7)
646 >>> u.configwith(float, s, b'unknown', 7)
615 7.0
647 7.0
616 >>> u.setconfig(s, b'invalid', b'somevalue')
648 >>> u.setconfig(s, b'invalid', b'somevalue')
617 >>> u.configwith(float, s, b'invalid')
649 >>> u.configwith(float, s, b'invalid')
618 Traceback (most recent call last):
650 Traceback (most recent call last):
619 ...
651 ...
620 ConfigError: foo.invalid is not a valid float ('somevalue')
652 ConfigError: foo.invalid is not a valid float ('somevalue')
621 >>> u.configwith(float, s, b'invalid', desc=b'womble')
653 >>> u.configwith(float, s, b'invalid', desc=b'womble')
622 Traceback (most recent call last):
654 Traceback (most recent call last):
623 ...
655 ...
624 ConfigError: foo.invalid is not a valid womble ('somevalue')
656 ConfigError: foo.invalid is not a valid womble ('somevalue')
625 """
657 """
626
658
627 v = self.config(section, name, default, untrusted)
659 v = self.config(section, name, default, untrusted)
628 if v is None:
660 if v is None:
629 return v # do not attempt to convert None
661 return v # do not attempt to convert None
630 try:
662 try:
631 return convert(v)
663 return convert(v)
632 except (ValueError, error.ParseError):
664 except (ValueError, error.ParseError):
633 if desc is None:
665 if desc is None:
634 desc = pycompat.sysbytes(convert.__name__)
666 desc = pycompat.sysbytes(convert.__name__)
635 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
667 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
636 % (section, name, desc, v))
668 % (section, name, desc, v))
637
669
638 def configint(self, section, name, default=_unset, untrusted=False):
670 def configint(self, section, name, default=_unset, untrusted=False):
639 """parse a configuration element as an integer
671 """parse a configuration element as an integer
640
672
641 >>> u = ui(); s = b'foo'
673 >>> u = ui(); s = b'foo'
642 >>> u.setconfig(s, b'int1', b'42')
674 >>> u.setconfig(s, b'int1', b'42')
643 >>> u.configint(s, b'int1')
675 >>> u.configint(s, b'int1')
644 42
676 42
645 >>> u.setconfig(s, b'int2', b'-42')
677 >>> u.setconfig(s, b'int2', b'-42')
646 >>> u.configint(s, b'int2')
678 >>> u.configint(s, b'int2')
647 -42
679 -42
648 >>> u.configint(s, b'unknown', 7)
680 >>> u.configint(s, b'unknown', 7)
649 7
681 7
650 >>> u.setconfig(s, b'invalid', b'somevalue')
682 >>> u.setconfig(s, b'invalid', b'somevalue')
651 >>> u.configint(s, b'invalid')
683 >>> u.configint(s, b'invalid')
652 Traceback (most recent call last):
684 Traceback (most recent call last):
653 ...
685 ...
654 ConfigError: foo.invalid is not a valid integer ('somevalue')
686 ConfigError: foo.invalid is not a valid integer ('somevalue')
655 """
687 """
656
688
657 return self.configwith(int, section, name, default, 'integer',
689 return self.configwith(int, section, name, default, 'integer',
658 untrusted)
690 untrusted)
659
691
660 def configbytes(self, section, name, default=_unset, untrusted=False):
692 def configbytes(self, section, name, default=_unset, untrusted=False):
661 """parse a configuration element as a quantity in bytes
693 """parse a configuration element as a quantity in bytes
662
694
663 Units can be specified as b (bytes), k or kb (kilobytes), m or
695 Units can be specified as b (bytes), k or kb (kilobytes), m or
664 mb (megabytes), g or gb (gigabytes).
696 mb (megabytes), g or gb (gigabytes).
665
697
666 >>> u = ui(); s = b'foo'
698 >>> u = ui(); s = b'foo'
667 >>> u.setconfig(s, b'val1', b'42')
699 >>> u.setconfig(s, b'val1', b'42')
668 >>> u.configbytes(s, b'val1')
700 >>> u.configbytes(s, b'val1')
669 42
701 42
670 >>> u.setconfig(s, b'val2', b'42.5 kb')
702 >>> u.setconfig(s, b'val2', b'42.5 kb')
671 >>> u.configbytes(s, b'val2')
703 >>> u.configbytes(s, b'val2')
672 43520
704 43520
673 >>> u.configbytes(s, b'unknown', b'7 MB')
705 >>> u.configbytes(s, b'unknown', b'7 MB')
674 7340032
706 7340032
675 >>> u.setconfig(s, b'invalid', b'somevalue')
707 >>> u.setconfig(s, b'invalid', b'somevalue')
676 >>> u.configbytes(s, b'invalid')
708 >>> u.configbytes(s, b'invalid')
677 Traceback (most recent call last):
709 Traceback (most recent call last):
678 ...
710 ...
679 ConfigError: foo.invalid is not a byte quantity ('somevalue')
711 ConfigError: foo.invalid is not a byte quantity ('somevalue')
680 """
712 """
681
713
682 value = self._config(section, name, default, untrusted)
714 value = self._config(section, name, default, untrusted)
683 if value is _unset:
715 if value is _unset:
684 if default is _unset:
716 if default is _unset:
685 default = 0
717 default = 0
686 value = default
718 value = default
687 if not isinstance(value, bytes):
719 if not isinstance(value, bytes):
688 return value
720 return value
689 try:
721 try:
690 return util.sizetoint(value)
722 return util.sizetoint(value)
691 except error.ParseError:
723 except error.ParseError:
692 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
724 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
693 % (section, name, value))
725 % (section, name, value))
694
726
695 def configlist(self, section, name, default=_unset, untrusted=False):
727 def configlist(self, section, name, default=_unset, untrusted=False):
696 """parse a configuration element as a list of comma/space separated
728 """parse a configuration element as a list of comma/space separated
697 strings
729 strings
698
730
699 >>> u = ui(); s = b'foo'
731 >>> u = ui(); s = b'foo'
700 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
732 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
701 >>> u.configlist(s, b'list1')
733 >>> u.configlist(s, b'list1')
702 ['this', 'is', 'a small', 'test']
734 ['this', 'is', 'a small', 'test']
703 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
735 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
704 >>> u.configlist(s, b'list2')
736 >>> u.configlist(s, b'list2')
705 ['this', 'is', 'a small', 'test']
737 ['this', 'is', 'a small', 'test']
706 """
738 """
707 # default is not always a list
739 # default is not always a list
708 v = self.configwith(config.parselist, section, name, default,
740 v = self.configwith(config.parselist, section, name, default,
709 'list', untrusted)
741 'list', untrusted)
710 if isinstance(v, bytes):
742 if isinstance(v, bytes):
711 return config.parselist(v)
743 return config.parselist(v)
712 elif v is None:
744 elif v is None:
713 return []
745 return []
714 return v
746 return v
715
747
716 def configdate(self, section, name, default=_unset, untrusted=False):
748 def configdate(self, section, name, default=_unset, untrusted=False):
717 """parse a configuration element as a tuple of ints
749 """parse a configuration element as a tuple of ints
718
750
719 >>> u = ui(); s = b'foo'
751 >>> u = ui(); s = b'foo'
720 >>> u.setconfig(s, b'date', b'0 0')
752 >>> u.setconfig(s, b'date', b'0 0')
721 >>> u.configdate(s, b'date')
753 >>> u.configdate(s, b'date')
722 (0, 0)
754 (0, 0)
723 """
755 """
724 if self.config(section, name, default, untrusted):
756 if self.config(section, name, default, untrusted):
725 return self.configwith(dateutil.parsedate, section, name, default,
757 return self.configwith(dateutil.parsedate, section, name, default,
726 'date', untrusted)
758 'date', untrusted)
727 if default is _unset:
759 if default is _unset:
728 return None
760 return None
729 return default
761 return default
730
762
731 def hasconfig(self, section, name, untrusted=False):
763 def hasconfig(self, section, name, untrusted=False):
732 return self._data(untrusted).hasitem(section, name)
764 return self._data(untrusted).hasitem(section, name)
733
765
734 def has_section(self, section, untrusted=False):
766 def has_section(self, section, untrusted=False):
735 '''tell whether section exists in config.'''
767 '''tell whether section exists in config.'''
736 return section in self._data(untrusted)
768 return section in self._data(untrusted)
737
769
738 def configitems(self, section, untrusted=False, ignoresub=False):
770 def configitems(self, section, untrusted=False, ignoresub=False):
739 items = self._data(untrusted).items(section)
771 items = self._data(untrusted).items(section)
740 if ignoresub:
772 if ignoresub:
741 items = [i for i in items if ':' not in i[0]]
773 items = [i for i in items if ':' not in i[0]]
742 if self.debugflag and not untrusted and self._reportuntrusted:
774 if self.debugflag and not untrusted and self._reportuntrusted:
743 for k, v in self._ucfg.items(section):
775 for k, v in self._ucfg.items(section):
744 if self._tcfg.get(section, k) != v:
776 if self._tcfg.get(section, k) != v:
745 self.debug("ignoring untrusted configuration option "
777 self.debug("ignoring untrusted configuration option "
746 "%s.%s = %s\n" % (section, k, v))
778 "%s.%s = %s\n" % (section, k, v))
747 return items
779 return items
748
780
749 def walkconfig(self, untrusted=False):
781 def walkconfig(self, untrusted=False):
750 cfg = self._data(untrusted)
782 cfg = self._data(untrusted)
751 for section in cfg.sections():
783 for section in cfg.sections():
752 for name, value in self.configitems(section, untrusted):
784 for name, value in self.configitems(section, untrusted):
753 yield section, name, value
785 yield section, name, value
754
786
755 def plain(self, feature=None):
787 def plain(self, feature=None):
756 '''is plain mode active?
788 '''is plain mode active?
757
789
758 Plain mode means that all configuration variables which affect
790 Plain mode means that all configuration variables which affect
759 the behavior and output of Mercurial should be
791 the behavior and output of Mercurial should be
760 ignored. Additionally, the output should be stable,
792 ignored. Additionally, the output should be stable,
761 reproducible and suitable for use in scripts or applications.
793 reproducible and suitable for use in scripts or applications.
762
794
763 The only way to trigger plain mode is by setting either the
795 The only way to trigger plain mode is by setting either the
764 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
796 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
765
797
766 The return value can either be
798 The return value can either be
767 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
799 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
768 - False if feature is disabled by default and not included in HGPLAIN
800 - False if feature is disabled by default and not included in HGPLAIN
769 - True otherwise
801 - True otherwise
770 '''
802 '''
771 if ('HGPLAIN' not in encoding.environ and
803 if ('HGPLAIN' not in encoding.environ and
772 'HGPLAINEXCEPT' not in encoding.environ):
804 'HGPLAINEXCEPT' not in encoding.environ):
773 return False
805 return False
774 exceptions = encoding.environ.get('HGPLAINEXCEPT',
806 exceptions = encoding.environ.get('HGPLAINEXCEPT',
775 '').strip().split(',')
807 '').strip().split(',')
776 # TODO: add support for HGPLAIN=+feature,-feature syntax
808 # TODO: add support for HGPLAIN=+feature,-feature syntax
777 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
809 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
778 exceptions.append('strictflags')
810 exceptions.append('strictflags')
779 if feature and exceptions:
811 if feature and exceptions:
780 return feature not in exceptions
812 return feature not in exceptions
781 return True
813 return True
782
814
783 def username(self, acceptempty=False):
815 def username(self, acceptempty=False):
784 """Return default username to be used in commits.
816 """Return default username to be used in commits.
785
817
786 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
818 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
787 and stop searching if one of these is set.
819 and stop searching if one of these is set.
788 If not found and acceptempty is True, returns None.
820 If not found and acceptempty is True, returns None.
789 If not found and ui.askusername is True, ask the user, else use
821 If not found and ui.askusername is True, ask the user, else use
790 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
822 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
791 If no username could be found, raise an Abort error.
823 If no username could be found, raise an Abort error.
792 """
824 """
793 user = encoding.environ.get("HGUSER")
825 user = encoding.environ.get("HGUSER")
794 if user is None:
826 if user is None:
795 user = self.config("ui", "username")
827 user = self.config("ui", "username")
796 if user is not None:
828 if user is not None:
797 user = os.path.expandvars(user)
829 user = os.path.expandvars(user)
798 if user is None:
830 if user is None:
799 user = encoding.environ.get("EMAIL")
831 user = encoding.environ.get("EMAIL")
800 if user is None and acceptempty:
832 if user is None and acceptempty:
801 return user
833 return user
802 if user is None and self.configbool("ui", "askusername"):
834 if user is None and self.configbool("ui", "askusername"):
803 user = self.prompt(_("enter a commit username:"), default=None)
835 user = self.prompt(_("enter a commit username:"), default=None)
804 if user is None and not self.interactive():
836 if user is None and not self.interactive():
805 try:
837 try:
806 user = '%s@%s' % (procutil.getuser(),
838 user = '%s@%s' % (procutil.getuser(),
807 encoding.strtolocal(socket.getfqdn()))
839 encoding.strtolocal(socket.getfqdn()))
808 self.warn(_("no username found, using '%s' instead\n") % user)
840 self.warn(_("no username found, using '%s' instead\n") % user)
809 except KeyError:
841 except KeyError:
810 pass
842 pass
811 if not user:
843 if not user:
812 raise error.Abort(_('no username supplied'),
844 raise error.Abort(_('no username supplied'),
813 hint=_("use 'hg config --edit' "
845 hint=_("use 'hg config --edit' "
814 'to set your username'))
846 'to set your username'))
815 if "\n" in user:
847 if "\n" in user:
816 raise error.Abort(_("username %r contains a newline\n")
848 raise error.Abort(_("username %r contains a newline\n")
817 % pycompat.bytestr(user))
849 % pycompat.bytestr(user))
818 return user
850 return user
819
851
820 def shortuser(self, user):
852 def shortuser(self, user):
821 """Return a short representation of a user name or email address."""
853 """Return a short representation of a user name or email address."""
822 if not self.verbose:
854 if not self.verbose:
823 user = stringutil.shortuser(user)
855 user = stringutil.shortuser(user)
824 return user
856 return user
825
857
826 def expandpath(self, loc, default=None):
858 def expandpath(self, loc, default=None):
827 """Return repository location relative to cwd or from [paths]"""
859 """Return repository location relative to cwd or from [paths]"""
828 try:
860 try:
829 p = self.paths.getpath(loc)
861 p = self.paths.getpath(loc)
830 if p:
862 if p:
831 return p.rawloc
863 return p.rawloc
832 except error.RepoError:
864 except error.RepoError:
833 pass
865 pass
834
866
835 if default:
867 if default:
836 try:
868 try:
837 p = self.paths.getpath(default)
869 p = self.paths.getpath(default)
838 if p:
870 if p:
839 return p.rawloc
871 return p.rawloc
840 except error.RepoError:
872 except error.RepoError:
841 pass
873 pass
842
874
843 return loc
875 return loc
844
876
845 @util.propertycache
877 @util.propertycache
846 def paths(self):
878 def paths(self):
847 return paths(self)
879 return paths(self)
848
880
849 def pushbuffer(self, error=False, subproc=False, labeled=False):
881 def pushbuffer(self, error=False, subproc=False, labeled=False):
850 """install a buffer to capture standard output of the ui object
882 """install a buffer to capture standard output of the ui object
851
883
852 If error is True, the error output will be captured too.
884 If error is True, the error output will be captured too.
853
885
854 If subproc is True, output from subprocesses (typically hooks) will be
886 If subproc is True, output from subprocesses (typically hooks) will be
855 captured too.
887 captured too.
856
888
857 If labeled is True, any labels associated with buffered
889 If labeled is True, any labels associated with buffered
858 output will be handled. By default, this has no effect
890 output will be handled. By default, this has no effect
859 on the output returned, but extensions and GUI tools may
891 on the output returned, but extensions and GUI tools may
860 handle this argument and returned styled output. If output
892 handle this argument and returned styled output. If output
861 is being buffered so it can be captured and parsed or
893 is being buffered so it can be captured and parsed or
862 processed, labeled should not be set to True.
894 processed, labeled should not be set to True.
863 """
895 """
864 self._buffers.append([])
896 self._buffers.append([])
865 self._bufferstates.append((error, subproc, labeled))
897 self._bufferstates.append((error, subproc, labeled))
866 self._bufferapplylabels = labeled
898 self._bufferapplylabels = labeled
867
899
868 def popbuffer(self):
900 def popbuffer(self):
869 '''pop the last buffer and return the buffered output'''
901 '''pop the last buffer and return the buffered output'''
870 self._bufferstates.pop()
902 self._bufferstates.pop()
871 if self._bufferstates:
903 if self._bufferstates:
872 self._bufferapplylabels = self._bufferstates[-1][2]
904 self._bufferapplylabels = self._bufferstates[-1][2]
873 else:
905 else:
874 self._bufferapplylabels = None
906 self._bufferapplylabels = None
875
907
876 return "".join(self._buffers.pop())
908 return "".join(self._buffers.pop())
877
909
878 def canwritewithoutlabels(self):
910 def canwritewithoutlabels(self):
879 '''check if write skips the label'''
911 '''check if write skips the label'''
880 if self._buffers and not self._bufferapplylabels:
912 if self._buffers and not self._bufferapplylabels:
881 return True
913 return True
882 return self._colormode is None
914 return self._colormode is None
883
915
884 def canbatchlabeledwrites(self):
916 def canbatchlabeledwrites(self):
885 '''check if write calls with labels are batchable'''
917 '''check if write calls with labels are batchable'''
886 # Windows color printing is special, see ``write``.
918 # Windows color printing is special, see ``write``.
887 return self._colormode != 'win32'
919 return self._colormode != 'win32'
888
920
889 def write(self, *args, **opts):
921 def write(self, *args, **opts):
890 '''write args to output
922 '''write args to output
891
923
892 By default, this method simply writes to the buffer or stdout.
924 By default, this method simply writes to the buffer or stdout.
893 Color mode can be set on the UI class to have the output decorated
925 Color mode can be set on the UI class to have the output decorated
894 with color modifier before being written to stdout.
926 with color modifier before being written to stdout.
895
927
896 The color used is controlled by an optional keyword argument, "label".
928 The color used is controlled by an optional keyword argument, "label".
897 This should be a string containing label names separated by space.
929 This should be a string containing label names separated by space.
898 Label names take the form of "topic.type". For example, ui.debug()
930 Label names take the form of "topic.type". For example, ui.debug()
899 issues a label of "ui.debug".
931 issues a label of "ui.debug".
900
932
901 When labeling output for a specific command, a label of
933 When labeling output for a specific command, a label of
902 "cmdname.type" is recommended. For example, status issues
934 "cmdname.type" is recommended. For example, status issues
903 a label of "status.modified" for modified files.
935 a label of "status.modified" for modified files.
904 '''
936 '''
905 if self._buffers:
937 if self._buffers:
906 if self._bufferapplylabels:
938 if self._bufferapplylabels:
907 label = opts.get(r'label', '')
939 label = opts.get(r'label', '')
908 self._buffers[-1].extend(self.label(a, label) for a in args)
940 self._buffers[-1].extend(self.label(a, label) for a in args)
909 else:
941 else:
910 self._buffers[-1].extend(args)
942 self._buffers[-1].extend(args)
911 else:
943 else:
912 self._writenobuf(*args, **opts)
944 self._writenobuf(*args, **opts)
913
945
914 def _writenobuf(self, *args, **opts):
946 def _writenobuf(self, *args, **opts):
915 if self._colormode == 'win32':
947 if self._colormode == 'win32':
916 # windows color printing is its own can of crab, defer to
948 # windows color printing is its own can of crab, defer to
917 # the color module and that is it.
949 # the color module and that is it.
918 color.win32print(self, self._write, *args, **opts)
950 color.win32print(self, self._write, *args, **opts)
919 else:
951 else:
920 msgs = args
952 msgs = args
921 if self._colormode is not None:
953 if self._colormode is not None:
922 label = opts.get(r'label', '')
954 label = opts.get(r'label', '')
923 msgs = [self.label(a, label) for a in args]
955 msgs = [self.label(a, label) for a in args]
924 self._write(*msgs, **opts)
956 self._write(*msgs, **opts)
925
957
926 def _write(self, *msgs, **opts):
958 def _write(self, *msgs, **opts):
927 self._progclear()
959 self._progclear()
928 # opencode timeblockedsection because this is a critical path
960 # opencode timeblockedsection because this is a critical path
929 starttime = util.timer()
961 starttime = util.timer()
930 try:
962 try:
931 self.fout.write(''.join(msgs))
963 self.fout.write(''.join(msgs))
932 except IOError as err:
964 except IOError as err:
933 raise error.StdioError(err)
965 raise error.StdioError(err)
934 finally:
966 finally:
935 self._blockedtimes['stdio_blocked'] += \
967 self._blockedtimes['stdio_blocked'] += \
936 (util.timer() - starttime) * 1000
968 (util.timer() - starttime) * 1000
937
969
938 def write_err(self, *args, **opts):
970 def write_err(self, *args, **opts):
939 self._progclear()
971 self._progclear()
940 if self._bufferstates and self._bufferstates[-1][0]:
972 if self._bufferstates and self._bufferstates[-1][0]:
941 self.write(*args, **opts)
973 self.write(*args, **opts)
942 elif self._colormode == 'win32':
974 elif self._colormode == 'win32':
943 # windows color printing is its own can of crab, defer to
975 # windows color printing is its own can of crab, defer to
944 # the color module and that is it.
976 # the color module and that is it.
945 color.win32print(self, self._write_err, *args, **opts)
977 color.win32print(self, self._write_err, *args, **opts)
946 else:
978 else:
947 msgs = args
979 msgs = args
948 if self._colormode is not None:
980 if self._colormode is not None:
949 label = opts.get(r'label', '')
981 label = opts.get(r'label', '')
950 msgs = [self.label(a, label) for a in args]
982 msgs = [self.label(a, label) for a in args]
951 self._write_err(*msgs, **opts)
983 self._write_err(*msgs, **opts)
952
984
953 def _write_err(self, *msgs, **opts):
985 def _write_err(self, *msgs, **opts):
954 try:
986 try:
955 with self.timeblockedsection('stdio'):
987 with self.timeblockedsection('stdio'):
956 if not getattr(self.fout, 'closed', False):
988 if not getattr(self.fout, 'closed', False):
957 self.fout.flush()
989 self.fout.flush()
958 for a in msgs:
990 for a in msgs:
959 self.ferr.write(a)
991 self.ferr.write(a)
960 # stderr may be buffered under win32 when redirected to files,
992 # stderr may be buffered under win32 when redirected to files,
961 # including stdout.
993 # including stdout.
962 if not getattr(self.ferr, 'closed', False):
994 if not getattr(self.ferr, 'closed', False):
963 self.ferr.flush()
995 self.ferr.flush()
964 except IOError as inst:
996 except IOError as inst:
965 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
997 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
966 raise error.StdioError(inst)
998 raise error.StdioError(inst)
967
999
968 def flush(self):
1000 def flush(self):
969 # opencode timeblockedsection because this is a critical path
1001 # opencode timeblockedsection because this is a critical path
970 starttime = util.timer()
1002 starttime = util.timer()
971 try:
1003 try:
972 try:
1004 try:
973 self.fout.flush()
1005 self.fout.flush()
974 except IOError as err:
1006 except IOError as err:
975 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1007 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
976 raise error.StdioError(err)
1008 raise error.StdioError(err)
977 finally:
1009 finally:
978 try:
1010 try:
979 self.ferr.flush()
1011 self.ferr.flush()
980 except IOError as err:
1012 except IOError as err:
981 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1013 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
982 raise error.StdioError(err)
1014 raise error.StdioError(err)
983 finally:
1015 finally:
984 self._blockedtimes['stdio_blocked'] += \
1016 self._blockedtimes['stdio_blocked'] += \
985 (util.timer() - starttime) * 1000
1017 (util.timer() - starttime) * 1000
986
1018
987 def _isatty(self, fh):
1019 def _isatty(self, fh):
988 if self.configbool('ui', 'nontty'):
1020 if self.configbool('ui', 'nontty'):
989 return False
1021 return False
990 return procutil.isatty(fh)
1022 return procutil.isatty(fh)
991
1023
992 def disablepager(self):
1024 def disablepager(self):
993 self._disablepager = True
1025 self._disablepager = True
994
1026
995 def pager(self, command):
1027 def pager(self, command):
996 """Start a pager for subsequent command output.
1028 """Start a pager for subsequent command output.
997
1029
998 Commands which produce a long stream of output should call
1030 Commands which produce a long stream of output should call
999 this function to activate the user's preferred pagination
1031 this function to activate the user's preferred pagination
1000 mechanism (which may be no pager). Calling this function
1032 mechanism (which may be no pager). Calling this function
1001 precludes any future use of interactive functionality, such as
1033 precludes any future use of interactive functionality, such as
1002 prompting the user or activating curses.
1034 prompting the user or activating curses.
1003
1035
1004 Args:
1036 Args:
1005 command: The full, non-aliased name of the command. That is, "log"
1037 command: The full, non-aliased name of the command. That is, "log"
1006 not "history, "summary" not "summ", etc.
1038 not "history, "summary" not "summ", etc.
1007 """
1039 """
1008 if (self._disablepager
1040 if (self._disablepager
1009 or self.pageractive):
1041 or self.pageractive):
1010 # how pager should do is already determined
1042 # how pager should do is already determined
1011 return
1043 return
1012
1044
1013 if not command.startswith('internal-always-') and (
1045 if not command.startswith('internal-always-') and (
1014 # explicit --pager=on (= 'internal-always-' prefix) should
1046 # explicit --pager=on (= 'internal-always-' prefix) should
1015 # take precedence over disabling factors below
1047 # take precedence over disabling factors below
1016 command in self.configlist('pager', 'ignore')
1048 command in self.configlist('pager', 'ignore')
1017 or not self.configbool('ui', 'paginate')
1049 or not self.configbool('ui', 'paginate')
1018 or not self.configbool('pager', 'attend-' + command, True)
1050 or not self.configbool('pager', 'attend-' + command, True)
1019 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1051 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1020 # formatted() will need some adjustment.
1052 # formatted() will need some adjustment.
1021 or not self.formatted()
1053 or not self.formatted()
1022 or self.plain()
1054 or self.plain()
1023 or self._buffers
1055 or self._buffers
1024 # TODO: expose debugger-enabled on the UI object
1056 # TODO: expose debugger-enabled on the UI object
1025 or '--debugger' in pycompat.sysargv):
1057 or '--debugger' in pycompat.sysargv):
1026 # We only want to paginate if the ui appears to be
1058 # We only want to paginate if the ui appears to be
1027 # interactive, the user didn't say HGPLAIN or
1059 # interactive, the user didn't say HGPLAIN or
1028 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1060 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1029 return
1061 return
1030
1062
1031 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1063 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1032 if not pagercmd:
1064 if not pagercmd:
1033 return
1065 return
1034
1066
1035 pagerenv = {}
1067 pagerenv = {}
1036 for name, value in rcutil.defaultpagerenv().items():
1068 for name, value in rcutil.defaultpagerenv().items():
1037 if name not in encoding.environ:
1069 if name not in encoding.environ:
1038 pagerenv[name] = value
1070 pagerenv[name] = value
1039
1071
1040 self.debug('starting pager for command %r\n' % command)
1072 self.debug('starting pager for command %r\n' % command)
1041 self.flush()
1073 self.flush()
1042
1074
1043 wasformatted = self.formatted()
1075 wasformatted = self.formatted()
1044 if util.safehasattr(signal, "SIGPIPE"):
1076 if util.safehasattr(signal, "SIGPIPE"):
1045 signal.signal(signal.SIGPIPE, _catchterm)
1077 signal.signal(signal.SIGPIPE, _catchterm)
1046 if self._runpager(pagercmd, pagerenv):
1078 if self._runpager(pagercmd, pagerenv):
1047 self.pageractive = True
1079 self.pageractive = True
1048 # Preserve the formatted-ness of the UI. This is important
1080 # Preserve the formatted-ness of the UI. This is important
1049 # because we mess with stdout, which might confuse
1081 # because we mess with stdout, which might confuse
1050 # auto-detection of things being formatted.
1082 # auto-detection of things being formatted.
1051 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1083 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1052 self.setconfig('ui', 'interactive', False, 'pager')
1084 self.setconfig('ui', 'interactive', False, 'pager')
1053
1085
1054 # If pagermode differs from color.mode, reconfigure color now that
1086 # If pagermode differs from color.mode, reconfigure color now that
1055 # pageractive is set.
1087 # pageractive is set.
1056 cm = self._colormode
1088 cm = self._colormode
1057 if cm != self.config('color', 'pagermode', cm):
1089 if cm != self.config('color', 'pagermode', cm):
1058 color.setup(self)
1090 color.setup(self)
1059 else:
1091 else:
1060 # If the pager can't be spawned in dispatch when --pager=on is
1092 # If the pager can't be spawned in dispatch when --pager=on is
1061 # given, don't try again when the command runs, to avoid a duplicate
1093 # given, don't try again when the command runs, to avoid a duplicate
1062 # warning about a missing pager command.
1094 # warning about a missing pager command.
1063 self.disablepager()
1095 self.disablepager()
1064
1096
1065 def _runpager(self, command, env=None):
1097 def _runpager(self, command, env=None):
1066 """Actually start the pager and set up file descriptors.
1098 """Actually start the pager and set up file descriptors.
1067
1099
1068 This is separate in part so that extensions (like chg) can
1100 This is separate in part so that extensions (like chg) can
1069 override how a pager is invoked.
1101 override how a pager is invoked.
1070 """
1102 """
1071 if command == 'cat':
1103 if command == 'cat':
1072 # Save ourselves some work.
1104 # Save ourselves some work.
1073 return False
1105 return False
1074 # If the command doesn't contain any of these characters, we
1106 # If the command doesn't contain any of these characters, we
1075 # assume it's a binary and exec it directly. This means for
1107 # assume it's a binary and exec it directly. This means for
1076 # simple pager command configurations, we can degrade
1108 # simple pager command configurations, we can degrade
1077 # gracefully and tell the user about their broken pager.
1109 # gracefully and tell the user about their broken pager.
1078 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1110 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1079
1111
1080 if pycompat.iswindows and not shell:
1112 if pycompat.iswindows and not shell:
1081 # Window's built-in `more` cannot be invoked with shell=False, but
1113 # Window's built-in `more` cannot be invoked with shell=False, but
1082 # its `more.com` can. Hide this implementation detail from the
1114 # its `more.com` can. Hide this implementation detail from the
1083 # user so we can also get sane bad PAGER behavior. MSYS has
1115 # user so we can also get sane bad PAGER behavior. MSYS has
1084 # `more.exe`, so do a cmd.exe style resolution of the executable to
1116 # `more.exe`, so do a cmd.exe style resolution of the executable to
1085 # determine which one to use.
1117 # determine which one to use.
1086 fullcmd = procutil.findexe(command)
1118 fullcmd = procutil.findexe(command)
1087 if not fullcmd:
1119 if not fullcmd:
1088 self.warn(_("missing pager command '%s', skipping pager\n")
1120 self.warn(_("missing pager command '%s', skipping pager\n")
1089 % command)
1121 % command)
1090 return False
1122 return False
1091
1123
1092 command = fullcmd
1124 command = fullcmd
1093
1125
1094 try:
1126 try:
1095 pager = subprocess.Popen(
1127 pager = subprocess.Popen(
1096 command, shell=shell, bufsize=-1,
1128 command, shell=shell, bufsize=-1,
1097 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1129 close_fds=procutil.closefds, stdin=subprocess.PIPE,
1098 stdout=procutil.stdout, stderr=procutil.stderr,
1130 stdout=procutil.stdout, stderr=procutil.stderr,
1099 env=procutil.shellenviron(env))
1131 env=procutil.shellenviron(env))
1100 except OSError as e:
1132 except OSError as e:
1101 if e.errno == errno.ENOENT and not shell:
1133 if e.errno == errno.ENOENT and not shell:
1102 self.warn(_("missing pager command '%s', skipping pager\n")
1134 self.warn(_("missing pager command '%s', skipping pager\n")
1103 % command)
1135 % command)
1104 return False
1136 return False
1105 raise
1137 raise
1106
1138
1107 # back up original file descriptors
1139 # back up original file descriptors
1108 stdoutfd = os.dup(procutil.stdout.fileno())
1140 stdoutfd = os.dup(procutil.stdout.fileno())
1109 stderrfd = os.dup(procutil.stderr.fileno())
1141 stderrfd = os.dup(procutil.stderr.fileno())
1110
1142
1111 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1143 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1112 if self._isatty(procutil.stderr):
1144 if self._isatty(procutil.stderr):
1113 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1145 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1114
1146
1115 @self.atexit
1147 @self.atexit
1116 def killpager():
1148 def killpager():
1117 if util.safehasattr(signal, "SIGINT"):
1149 if util.safehasattr(signal, "SIGINT"):
1118 signal.signal(signal.SIGINT, signal.SIG_IGN)
1150 signal.signal(signal.SIGINT, signal.SIG_IGN)
1119 # restore original fds, closing pager.stdin copies in the process
1151 # restore original fds, closing pager.stdin copies in the process
1120 os.dup2(stdoutfd, procutil.stdout.fileno())
1152 os.dup2(stdoutfd, procutil.stdout.fileno())
1121 os.dup2(stderrfd, procutil.stderr.fileno())
1153 os.dup2(stderrfd, procutil.stderr.fileno())
1122 pager.stdin.close()
1154 pager.stdin.close()
1123 pager.wait()
1155 pager.wait()
1124
1156
1125 return True
1157 return True
1126
1158
1127 @property
1159 @property
1128 def _exithandlers(self):
1160 def _exithandlers(self):
1129 return _reqexithandlers
1161 return _reqexithandlers
1130
1162
1131 def atexit(self, func, *args, **kwargs):
1163 def atexit(self, func, *args, **kwargs):
1132 '''register a function to run after dispatching a request
1164 '''register a function to run after dispatching a request
1133
1165
1134 Handlers do not stay registered across request boundaries.'''
1166 Handlers do not stay registered across request boundaries.'''
1135 self._exithandlers.append((func, args, kwargs))
1167 self._exithandlers.append((func, args, kwargs))
1136 return func
1168 return func
1137
1169
1138 def interface(self, feature):
1170 def interface(self, feature):
1139 """what interface to use for interactive console features?
1171 """what interface to use for interactive console features?
1140
1172
1141 The interface is controlled by the value of `ui.interface` but also by
1173 The interface is controlled by the value of `ui.interface` but also by
1142 the value of feature-specific configuration. For example:
1174 the value of feature-specific configuration. For example:
1143
1175
1144 ui.interface.histedit = text
1176 ui.interface.histedit = text
1145 ui.interface.chunkselector = curses
1177 ui.interface.chunkselector = curses
1146
1178
1147 Here the features are "histedit" and "chunkselector".
1179 Here the features are "histedit" and "chunkselector".
1148
1180
1149 The configuration above means that the default interfaces for commands
1181 The configuration above means that the default interfaces for commands
1150 is curses, the interface for histedit is text and the interface for
1182 is curses, the interface for histedit is text and the interface for
1151 selecting chunk is crecord (the best curses interface available).
1183 selecting chunk is crecord (the best curses interface available).
1152
1184
1153 Consider the following example:
1185 Consider the following example:
1154 ui.interface = curses
1186 ui.interface = curses
1155 ui.interface.histedit = text
1187 ui.interface.histedit = text
1156
1188
1157 Then histedit will use the text interface and chunkselector will use
1189 Then histedit will use the text interface and chunkselector will use
1158 the default curses interface (crecord at the moment).
1190 the default curses interface (crecord at the moment).
1159 """
1191 """
1160 alldefaults = frozenset(["text", "curses"])
1192 alldefaults = frozenset(["text", "curses"])
1161
1193
1162 featureinterfaces = {
1194 featureinterfaces = {
1163 "chunkselector": [
1195 "chunkselector": [
1164 "text",
1196 "text",
1165 "curses",
1197 "curses",
1166 ]
1198 ]
1167 }
1199 }
1168
1200
1169 # Feature-specific interface
1201 # Feature-specific interface
1170 if feature not in featureinterfaces.keys():
1202 if feature not in featureinterfaces.keys():
1171 # Programming error, not user error
1203 # Programming error, not user error
1172 raise ValueError("Unknown feature requested %s" % feature)
1204 raise ValueError("Unknown feature requested %s" % feature)
1173
1205
1174 availableinterfaces = frozenset(featureinterfaces[feature])
1206 availableinterfaces = frozenset(featureinterfaces[feature])
1175 if alldefaults > availableinterfaces:
1207 if alldefaults > availableinterfaces:
1176 # Programming error, not user error. We need a use case to
1208 # Programming error, not user error. We need a use case to
1177 # define the right thing to do here.
1209 # define the right thing to do here.
1178 raise ValueError(
1210 raise ValueError(
1179 "Feature %s does not handle all default interfaces" %
1211 "Feature %s does not handle all default interfaces" %
1180 feature)
1212 feature)
1181
1213
1182 if self.plain():
1214 if self.plain():
1183 return "text"
1215 return "text"
1184
1216
1185 # Default interface for all the features
1217 # Default interface for all the features
1186 defaultinterface = "text"
1218 defaultinterface = "text"
1187 i = self.config("ui", "interface")
1219 i = self.config("ui", "interface")
1188 if i in alldefaults:
1220 if i in alldefaults:
1189 defaultinterface = i
1221 defaultinterface = i
1190
1222
1191 choseninterface = defaultinterface
1223 choseninterface = defaultinterface
1192 f = self.config("ui", "interface.%s" % feature)
1224 f = self.config("ui", "interface.%s" % feature)
1193 if f in availableinterfaces:
1225 if f in availableinterfaces:
1194 choseninterface = f
1226 choseninterface = f
1195
1227
1196 if i is not None and defaultinterface != i:
1228 if i is not None and defaultinterface != i:
1197 if f is not None:
1229 if f is not None:
1198 self.warn(_("invalid value for ui.interface: %s\n") %
1230 self.warn(_("invalid value for ui.interface: %s\n") %
1199 (i,))
1231 (i,))
1200 else:
1232 else:
1201 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1233 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1202 (i, choseninterface))
1234 (i, choseninterface))
1203 if f is not None and choseninterface != f:
1235 if f is not None and choseninterface != f:
1204 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1236 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1205 (feature, f, choseninterface))
1237 (feature, f, choseninterface))
1206
1238
1207 return choseninterface
1239 return choseninterface
1208
1240
1209 def interactive(self):
1241 def interactive(self):
1210 '''is interactive input allowed?
1242 '''is interactive input allowed?
1211
1243
1212 An interactive session is a session where input can be reasonably read
1244 An interactive session is a session where input can be reasonably read
1213 from `sys.stdin'. If this function returns false, any attempt to read
1245 from `sys.stdin'. If this function returns false, any attempt to read
1214 from stdin should fail with an error, unless a sensible default has been
1246 from stdin should fail with an error, unless a sensible default has been
1215 specified.
1247 specified.
1216
1248
1217 Interactiveness is triggered by the value of the `ui.interactive'
1249 Interactiveness is triggered by the value of the `ui.interactive'
1218 configuration variable or - if it is unset - when `sys.stdin' points
1250 configuration variable or - if it is unset - when `sys.stdin' points
1219 to a terminal device.
1251 to a terminal device.
1220
1252
1221 This function refers to input only; for output, see `ui.formatted()'.
1253 This function refers to input only; for output, see `ui.formatted()'.
1222 '''
1254 '''
1223 i = self.configbool("ui", "interactive")
1255 i = self.configbool("ui", "interactive")
1224 if i is None:
1256 if i is None:
1225 # some environments replace stdin without implementing isatty
1257 # some environments replace stdin without implementing isatty
1226 # usually those are non-interactive
1258 # usually those are non-interactive
1227 return self._isatty(self.fin)
1259 return self._isatty(self.fin)
1228
1260
1229 return i
1261 return i
1230
1262
1231 def termwidth(self):
1263 def termwidth(self):
1232 '''how wide is the terminal in columns?
1264 '''how wide is the terminal in columns?
1233 '''
1265 '''
1234 if 'COLUMNS' in encoding.environ:
1266 if 'COLUMNS' in encoding.environ:
1235 try:
1267 try:
1236 return int(encoding.environ['COLUMNS'])
1268 return int(encoding.environ['COLUMNS'])
1237 except ValueError:
1269 except ValueError:
1238 pass
1270 pass
1239 return scmutil.termsize(self)[0]
1271 return scmutil.termsize(self)[0]
1240
1272
1241 def formatted(self):
1273 def formatted(self):
1242 '''should formatted output be used?
1274 '''should formatted output be used?
1243
1275
1244 It is often desirable to format the output to suite the output medium.
1276 It is often desirable to format the output to suite the output medium.
1245 Examples of this are truncating long lines or colorizing messages.
1277 Examples of this are truncating long lines or colorizing messages.
1246 However, this is not often not desirable when piping output into other
1278 However, this is not often not desirable when piping output into other
1247 utilities, e.g. `grep'.
1279 utilities, e.g. `grep'.
1248
1280
1249 Formatted output is triggered by the value of the `ui.formatted'
1281 Formatted output is triggered by the value of the `ui.formatted'
1250 configuration variable or - if it is unset - when `sys.stdout' points
1282 configuration variable or - if it is unset - when `sys.stdout' points
1251 to a terminal device. Please note that `ui.formatted' should be
1283 to a terminal device. Please note that `ui.formatted' should be
1252 considered an implementation detail; it is not intended for use outside
1284 considered an implementation detail; it is not intended for use outside
1253 Mercurial or its extensions.
1285 Mercurial or its extensions.
1254
1286
1255 This function refers to output only; for input, see `ui.interactive()'.
1287 This function refers to output only; for input, see `ui.interactive()'.
1256 This function always returns false when in plain mode, see `ui.plain()'.
1288 This function always returns false when in plain mode, see `ui.plain()'.
1257 '''
1289 '''
1258 if self.plain():
1290 if self.plain():
1259 return False
1291 return False
1260
1292
1261 i = self.configbool("ui", "formatted")
1293 i = self.configbool("ui", "formatted")
1262 if i is None:
1294 if i is None:
1263 # some environments replace stdout without implementing isatty
1295 # some environments replace stdout without implementing isatty
1264 # usually those are non-interactive
1296 # usually those are non-interactive
1265 return self._isatty(self.fout)
1297 return self._isatty(self.fout)
1266
1298
1267 return i
1299 return i
1268
1300
1269 def _readline(self):
1301 def _readline(self):
1270 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1302 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1271 # because they have to be text streams with *no buffering*. Instead,
1303 # because they have to be text streams with *no buffering*. Instead,
1272 # we use rawinput() only if call_readline() will be invoked by
1304 # we use rawinput() only if call_readline() will be invoked by
1273 # PyOS_Readline(), so no I/O will be made at Python layer.
1305 # PyOS_Readline(), so no I/O will be made at Python layer.
1274 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1306 usereadline = (self._isatty(self.fin) and self._isatty(self.fout)
1275 and procutil.isstdin(self.fin)
1307 and procutil.isstdin(self.fin)
1276 and procutil.isstdout(self.fout))
1308 and procutil.isstdout(self.fout))
1277 if usereadline:
1309 if usereadline:
1278 try:
1310 try:
1279 # magically add command line editing support, where
1311 # magically add command line editing support, where
1280 # available
1312 # available
1281 import readline
1313 import readline
1282 # force demandimport to really load the module
1314 # force demandimport to really load the module
1283 readline.read_history_file
1315 readline.read_history_file
1284 # windows sometimes raises something other than ImportError
1316 # windows sometimes raises something other than ImportError
1285 except Exception:
1317 except Exception:
1286 usereadline = False
1318 usereadline = False
1287
1319
1288 # prompt ' ' must exist; otherwise readline may delete entire line
1320 # prompt ' ' must exist; otherwise readline may delete entire line
1289 # - http://bugs.python.org/issue12833
1321 # - http://bugs.python.org/issue12833
1290 with self.timeblockedsection('stdio'):
1322 with self.timeblockedsection('stdio'):
1291 if usereadline:
1323 if usereadline:
1292 line = encoding.strtolocal(pycompat.rawinput(r' '))
1324 line = encoding.strtolocal(pycompat.rawinput(r' '))
1293 # When stdin is in binary mode on Windows, it can cause
1325 # When stdin is in binary mode on Windows, it can cause
1294 # raw_input() to emit an extra trailing carriage return
1326 # raw_input() to emit an extra trailing carriage return
1295 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1327 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1296 line = line[:-1]
1328 line = line[:-1]
1297 else:
1329 else:
1298 self.fout.write(b' ')
1330 self.fout.write(b' ')
1299 self.fout.flush()
1331 self.fout.flush()
1300 line = self.fin.readline()
1332 line = self.fin.readline()
1301 if not line:
1333 if not line:
1302 raise EOFError
1334 raise EOFError
1303 line = line.rstrip(pycompat.oslinesep)
1335 line = line.rstrip(pycompat.oslinesep)
1304
1336
1305 return line
1337 return line
1306
1338
1307 def prompt(self, msg, default="y"):
1339 def prompt(self, msg, default="y"):
1308 """Prompt user with msg, read response.
1340 """Prompt user with msg, read response.
1309 If ui is not interactive, the default is returned.
1341 If ui is not interactive, the default is returned.
1310 """
1342 """
1311 if not self.interactive():
1343 if not self.interactive():
1312 self.write(msg, ' ', default or '', "\n")
1344 self.write(msg, ' ', default or '', "\n")
1313 return default
1345 return default
1314 self._writenobuf(msg, label='ui.prompt')
1346 self._writenobuf(msg, label='ui.prompt')
1315 self.flush()
1347 self.flush()
1316 try:
1348 try:
1317 r = self._readline()
1349 r = self._readline()
1318 if not r:
1350 if not r:
1319 r = default
1351 r = default
1320 if self.configbool('ui', 'promptecho'):
1352 if self.configbool('ui', 'promptecho'):
1321 self.write(r, "\n")
1353 self.write(r, "\n")
1322 return r
1354 return r
1323 except EOFError:
1355 except EOFError:
1324 raise error.ResponseExpected()
1356 raise error.ResponseExpected()
1325
1357
1326 @staticmethod
1358 @staticmethod
1327 def extractchoices(prompt):
1359 def extractchoices(prompt):
1328 """Extract prompt message and list of choices from specified prompt.
1360 """Extract prompt message and list of choices from specified prompt.
1329
1361
1330 This returns tuple "(message, choices)", and "choices" is the
1362 This returns tuple "(message, choices)", and "choices" is the
1331 list of tuple "(response character, text without &)".
1363 list of tuple "(response character, text without &)".
1332
1364
1333 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1365 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1334 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1366 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1335 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1367 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1336 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1368 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1337 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1369 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1338 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1370 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1339 """
1371 """
1340
1372
1341 # Sadly, the prompt string may have been built with a filename
1373 # Sadly, the prompt string may have been built with a filename
1342 # containing "$$" so let's try to find the first valid-looking
1374 # containing "$$" so let's try to find the first valid-looking
1343 # prompt to start parsing. Sadly, we also can't rely on
1375 # prompt to start parsing. Sadly, we also can't rely on
1344 # choices containing spaces, ASCII, or basically anything
1376 # choices containing spaces, ASCII, or basically anything
1345 # except an ampersand followed by a character.
1377 # except an ampersand followed by a character.
1346 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1378 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1347 msg = m.group(1)
1379 msg = m.group(1)
1348 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1380 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1349 def choicetuple(s):
1381 def choicetuple(s):
1350 ampidx = s.index('&')
1382 ampidx = s.index('&')
1351 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1383 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1352 return (msg, [choicetuple(s) for s in choices])
1384 return (msg, [choicetuple(s) for s in choices])
1353
1385
1354 def promptchoice(self, prompt, default=0):
1386 def promptchoice(self, prompt, default=0):
1355 """Prompt user with a message, read response, and ensure it matches
1387 """Prompt user with a message, read response, and ensure it matches
1356 one of the provided choices. The prompt is formatted as follows:
1388 one of the provided choices. The prompt is formatted as follows:
1357
1389
1358 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1390 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1359
1391
1360 The index of the choice is returned. Responses are case
1392 The index of the choice is returned. Responses are case
1361 insensitive. If ui is not interactive, the default is
1393 insensitive. If ui is not interactive, the default is
1362 returned.
1394 returned.
1363 """
1395 """
1364
1396
1365 msg, choices = self.extractchoices(prompt)
1397 msg, choices = self.extractchoices(prompt)
1366 resps = [r for r, t in choices]
1398 resps = [r for r, t in choices]
1367 while True:
1399 while True:
1368 r = self.prompt(msg, resps[default])
1400 r = self.prompt(msg, resps[default])
1369 if r.lower() in resps:
1401 if r.lower() in resps:
1370 return resps.index(r.lower())
1402 return resps.index(r.lower())
1371 self.write(_("unrecognized response\n"))
1403 self.write(_("unrecognized response\n"))
1372
1404
1373 def getpass(self, prompt=None, default=None):
1405 def getpass(self, prompt=None, default=None):
1374 if not self.interactive():
1406 if not self.interactive():
1375 return default
1407 return default
1376 try:
1408 try:
1377 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1409 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1378 # disable getpass() only if explicitly specified. it's still valid
1410 # disable getpass() only if explicitly specified. it's still valid
1379 # to interact with tty even if fin is not a tty.
1411 # to interact with tty even if fin is not a tty.
1380 with self.timeblockedsection('stdio'):
1412 with self.timeblockedsection('stdio'):
1381 if self.configbool('ui', 'nontty'):
1413 if self.configbool('ui', 'nontty'):
1382 l = self.fin.readline()
1414 l = self.fin.readline()
1383 if not l:
1415 if not l:
1384 raise EOFError
1416 raise EOFError
1385 return l.rstrip('\n')
1417 return l.rstrip('\n')
1386 else:
1418 else:
1387 return getpass.getpass('')
1419 return getpass.getpass('')
1388 except EOFError:
1420 except EOFError:
1389 raise error.ResponseExpected()
1421 raise error.ResponseExpected()
1390 def status(self, *msg, **opts):
1422 def status(self, *msg, **opts):
1391 '''write status message to output (if ui.quiet is False)
1423 '''write status message to output (if ui.quiet is False)
1392
1424
1393 This adds an output label of "ui.status".
1425 This adds an output label of "ui.status".
1394 '''
1426 '''
1395 if not self.quiet:
1427 if not self.quiet:
1396 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1428 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1397 self.write(*msg, **opts)
1429 self.write(*msg, **opts)
1398 def warn(self, *msg, **opts):
1430 def warn(self, *msg, **opts):
1399 '''write warning message to output (stderr)
1431 '''write warning message to output (stderr)
1400
1432
1401 This adds an output label of "ui.warning".
1433 This adds an output label of "ui.warning".
1402 '''
1434 '''
1403 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1435 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1404 self.write_err(*msg, **opts)
1436 self.write_err(*msg, **opts)
1405 def note(self, *msg, **opts):
1437 def note(self, *msg, **opts):
1406 '''write note to output (if ui.verbose is True)
1438 '''write note to output (if ui.verbose is True)
1407
1439
1408 This adds an output label of "ui.note".
1440 This adds an output label of "ui.note".
1409 '''
1441 '''
1410 if self.verbose:
1442 if self.verbose:
1411 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1443 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1412 self.write(*msg, **opts)
1444 self.write(*msg, **opts)
1413 def debug(self, *msg, **opts):
1445 def debug(self, *msg, **opts):
1414 '''write debug message to output (if ui.debugflag is True)
1446 '''write debug message to output (if ui.debugflag is True)
1415
1447
1416 This adds an output label of "ui.debug".
1448 This adds an output label of "ui.debug".
1417 '''
1449 '''
1418 if self.debugflag:
1450 if self.debugflag:
1419 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1451 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1420 self.write(*msg, **opts)
1452 self.write(*msg, **opts)
1421
1453
1422 def edit(self, text, user, extra=None, editform=None, pending=None,
1454 def edit(self, text, user, extra=None, editform=None, pending=None,
1423 repopath=None, action=None):
1455 repopath=None, action=None):
1424 if action is None:
1456 if action is None:
1425 self.develwarn('action is None but will soon be a required '
1457 self.develwarn('action is None but will soon be a required '
1426 'parameter to ui.edit()')
1458 'parameter to ui.edit()')
1427 extra_defaults = {
1459 extra_defaults = {
1428 'prefix': 'editor',
1460 'prefix': 'editor',
1429 'suffix': '.txt',
1461 'suffix': '.txt',
1430 }
1462 }
1431 if extra is not None:
1463 if extra is not None:
1432 if extra.get('suffix') is not None:
1464 if extra.get('suffix') is not None:
1433 self.develwarn('extra.suffix is not None but will soon be '
1465 self.develwarn('extra.suffix is not None but will soon be '
1434 'ignored by ui.edit()')
1466 'ignored by ui.edit()')
1435 extra_defaults.update(extra)
1467 extra_defaults.update(extra)
1436 extra = extra_defaults
1468 extra = extra_defaults
1437
1469
1438 if action == 'diff':
1470 if action == 'diff':
1439 suffix = '.diff'
1471 suffix = '.diff'
1440 elif action:
1472 elif action:
1441 suffix = '.%s.hg.txt' % action
1473 suffix = '.%s.hg.txt' % action
1442 else:
1474 else:
1443 suffix = extra['suffix']
1475 suffix = extra['suffix']
1444
1476
1445 rdir = None
1477 rdir = None
1446 if self.configbool('experimental', 'editortmpinhg'):
1478 if self.configbool('experimental', 'editortmpinhg'):
1447 rdir = repopath
1479 rdir = repopath
1448 (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1480 (fd, name) = pycompat.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1449 suffix=suffix,
1481 suffix=suffix,
1450 dir=rdir)
1482 dir=rdir)
1451 try:
1483 try:
1452 f = os.fdopen(fd, r'wb')
1484 f = os.fdopen(fd, r'wb')
1453 f.write(util.tonativeeol(text))
1485 f.write(util.tonativeeol(text))
1454 f.close()
1486 f.close()
1455
1487
1456 environ = {'HGUSER': user}
1488 environ = {'HGUSER': user}
1457 if 'transplant_source' in extra:
1489 if 'transplant_source' in extra:
1458 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1490 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1459 for label in ('intermediate-source', 'source', 'rebase_source'):
1491 for label in ('intermediate-source', 'source', 'rebase_source'):
1460 if label in extra:
1492 if label in extra:
1461 environ.update({'HGREVISION': extra[label]})
1493 environ.update({'HGREVISION': extra[label]})
1462 break
1494 break
1463 if editform:
1495 if editform:
1464 environ.update({'HGEDITFORM': editform})
1496 environ.update({'HGEDITFORM': editform})
1465 if pending:
1497 if pending:
1466 environ.update({'HG_PENDING': pending})
1498 environ.update({'HG_PENDING': pending})
1467
1499
1468 editor = self.geteditor()
1500 editor = self.geteditor()
1469
1501
1470 self.system("%s \"%s\"" % (editor, name),
1502 self.system("%s \"%s\"" % (editor, name),
1471 environ=environ,
1503 environ=environ,
1472 onerr=error.Abort, errprefix=_("edit failed"),
1504 onerr=error.Abort, errprefix=_("edit failed"),
1473 blockedtag='editor')
1505 blockedtag='editor')
1474
1506
1475 f = open(name, r'rb')
1507 f = open(name, r'rb')
1476 t = util.fromnativeeol(f.read())
1508 t = util.fromnativeeol(f.read())
1477 f.close()
1509 f.close()
1478 finally:
1510 finally:
1479 os.unlink(name)
1511 os.unlink(name)
1480
1512
1481 return t
1513 return t
1482
1514
1483 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1515 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1484 blockedtag=None):
1516 blockedtag=None):
1485 '''execute shell command with appropriate output stream. command
1517 '''execute shell command with appropriate output stream. command
1486 output will be redirected if fout is not stdout.
1518 output will be redirected if fout is not stdout.
1487
1519
1488 if command fails and onerr is None, return status, else raise onerr
1520 if command fails and onerr is None, return status, else raise onerr
1489 object as exception.
1521 object as exception.
1490 '''
1522 '''
1491 if blockedtag is None:
1523 if blockedtag is None:
1492 # Long cmds tend to be because of an absolute path on cmd. Keep
1524 # Long cmds tend to be because of an absolute path on cmd. Keep
1493 # the tail end instead
1525 # the tail end instead
1494 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1526 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1495 blockedtag = 'unknown_system_' + cmdsuffix
1527 blockedtag = 'unknown_system_' + cmdsuffix
1496 out = self.fout
1528 out = self.fout
1497 if any(s[1] for s in self._bufferstates):
1529 if any(s[1] for s in self._bufferstates):
1498 out = self
1530 out = self
1499 with self.timeblockedsection(blockedtag):
1531 with self.timeblockedsection(blockedtag):
1500 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1532 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1501 if rc and onerr:
1533 if rc and onerr:
1502 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1534 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1503 procutil.explainexit(rc))
1535 procutil.explainexit(rc))
1504 if errprefix:
1536 if errprefix:
1505 errmsg = '%s: %s' % (errprefix, errmsg)
1537 errmsg = '%s: %s' % (errprefix, errmsg)
1506 raise onerr(errmsg)
1538 raise onerr(errmsg)
1507 return rc
1539 return rc
1508
1540
1509 def _runsystem(self, cmd, environ, cwd, out):
1541 def _runsystem(self, cmd, environ, cwd, out):
1510 """actually execute the given shell command (can be overridden by
1542 """actually execute the given shell command (can be overridden by
1511 extensions like chg)"""
1543 extensions like chg)"""
1512 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1544 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1513
1545
1514 def traceback(self, exc=None, force=False):
1546 def traceback(self, exc=None, force=False):
1515 '''print exception traceback if traceback printing enabled or forced.
1547 '''print exception traceback if traceback printing enabled or forced.
1516 only to call in exception handler. returns true if traceback
1548 only to call in exception handler. returns true if traceback
1517 printed.'''
1549 printed.'''
1518 if self.tracebackflag or force:
1550 if self.tracebackflag or force:
1519 if exc is None:
1551 if exc is None:
1520 exc = sys.exc_info()
1552 exc = sys.exc_info()
1521 cause = getattr(exc[1], 'cause', None)
1553 cause = getattr(exc[1], 'cause', None)
1522
1554
1523 if cause is not None:
1555 if cause is not None:
1524 causetb = traceback.format_tb(cause[2])
1556 causetb = traceback.format_tb(cause[2])
1525 exctb = traceback.format_tb(exc[2])
1557 exctb = traceback.format_tb(exc[2])
1526 exconly = traceback.format_exception_only(cause[0], cause[1])
1558 exconly = traceback.format_exception_only(cause[0], cause[1])
1527
1559
1528 # exclude frame where 'exc' was chained and rethrown from exctb
1560 # exclude frame where 'exc' was chained and rethrown from exctb
1529 self.write_err('Traceback (most recent call last):\n',
1561 self.write_err('Traceback (most recent call last):\n',
1530 ''.join(exctb[:-1]),
1562 ''.join(exctb[:-1]),
1531 ''.join(causetb),
1563 ''.join(causetb),
1532 ''.join(exconly))
1564 ''.join(exconly))
1533 else:
1565 else:
1534 output = traceback.format_exception(exc[0], exc[1], exc[2])
1566 output = traceback.format_exception(exc[0], exc[1], exc[2])
1535 self.write_err(encoding.strtolocal(r''.join(output)))
1567 self.write_err(encoding.strtolocal(r''.join(output)))
1536 return self.tracebackflag or force
1568 return self.tracebackflag or force
1537
1569
1538 def geteditor(self):
1570 def geteditor(self):
1539 '''return editor to use'''
1571 '''return editor to use'''
1540 if pycompat.sysplatform == 'plan9':
1572 if pycompat.sysplatform == 'plan9':
1541 # vi is the MIPS instruction simulator on Plan 9. We
1573 # vi is the MIPS instruction simulator on Plan 9. We
1542 # instead default to E to plumb commit messages to
1574 # instead default to E to plumb commit messages to
1543 # avoid confusion.
1575 # avoid confusion.
1544 editor = 'E'
1576 editor = 'E'
1545 else:
1577 else:
1546 editor = 'vi'
1578 editor = 'vi'
1547 return (encoding.environ.get("HGEDITOR") or
1579 return (encoding.environ.get("HGEDITOR") or
1548 self.config("ui", "editor", editor))
1580 self.config("ui", "editor", editor))
1549
1581
1550 @util.propertycache
1582 @util.propertycache
1551 def _progbar(self):
1583 def _progbar(self):
1552 """setup the progbar singleton to the ui object"""
1584 """setup the progbar singleton to the ui object"""
1553 if (self.quiet or self.debugflag
1585 if (self.quiet or self.debugflag
1554 or self.configbool('progress', 'disable')
1586 or self.configbool('progress', 'disable')
1555 or not progress.shouldprint(self)):
1587 or not progress.shouldprint(self)):
1556 return None
1588 return None
1557 return getprogbar(self)
1589 return getprogbar(self)
1558
1590
1559 def _progclear(self):
1591 def _progclear(self):
1560 """clear progress bar output if any. use it before any output"""
1592 """clear progress bar output if any. use it before any output"""
1561 if not haveprogbar(): # nothing loaded yet
1593 if not haveprogbar(): # nothing loaded yet
1562 return
1594 return
1563 if self._progbar is not None and self._progbar.printed:
1595 if self._progbar is not None and self._progbar.printed:
1564 self._progbar.clear()
1596 self._progbar.clear()
1565
1597
1566 def progress(self, topic, pos, item="", unit="", total=None):
1598 def progress(self, topic, pos, item="", unit="", total=None):
1567 '''show a progress message
1599 '''show a progress message
1568
1600
1569 By default a textual progress bar will be displayed if an operation
1601 By default a textual progress bar will be displayed if an operation
1570 takes too long. 'topic' is the current operation, 'item' is a
1602 takes too long. 'topic' is the current operation, 'item' is a
1571 non-numeric marker of the current position (i.e. the currently
1603 non-numeric marker of the current position (i.e. the currently
1572 in-process file), 'pos' is the current numeric position (i.e.
1604 in-process file), 'pos' is the current numeric position (i.e.
1573 revision, bytes, etc.), unit is a corresponding unit label,
1605 revision, bytes, etc.), unit is a corresponding unit label,
1574 and total is the highest expected pos.
1606 and total is the highest expected pos.
1575
1607
1576 Multiple nested topics may be active at a time.
1608 Multiple nested topics may be active at a time.
1577
1609
1578 All topics should be marked closed by setting pos to None at
1610 All topics should be marked closed by setting pos to None at
1579 termination.
1611 termination.
1580 '''
1612 '''
1581 if self._progbar is not None:
1613 if self._progbar is not None:
1582 self._progbar.progress(topic, pos, item=item, unit=unit,
1614 self._progbar.progress(topic, pos, item=item, unit=unit,
1583 total=total)
1615 total=total)
1584 if pos is None or not self.configbool('progress', 'debug'):
1616 if pos is None or not self.configbool('progress', 'debug'):
1585 return
1617 return
1586
1618
1587 if unit:
1619 if unit:
1588 unit = ' ' + unit
1620 unit = ' ' + unit
1589 if item:
1621 if item:
1590 item = ' ' + item
1622 item = ' ' + item
1591
1623
1592 if total:
1624 if total:
1593 pct = 100.0 * pos / total
1625 pct = 100.0 * pos / total
1594 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1626 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1595 % (topic, item, pos, total, unit, pct))
1627 % (topic, item, pos, total, unit, pct))
1596 else:
1628 else:
1597 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1629 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1598
1630
1599 def makeprogress(self, topic, unit="", total=None):
1631 def makeprogress(self, topic, unit="", total=None):
1600 '''exists only so low-level modules won't need to import scmutil'''
1632 '''exists only so low-level modules won't need to import scmutil'''
1601 return scmutil.progress(self, topic, unit, total)
1633 return scmutil.progress(self, topic, unit, total)
1602
1634
1603 def log(self, service, *msg, **opts):
1635 def log(self, service, *msg, **opts):
1604 '''hook for logging facility extensions
1636 '''hook for logging facility extensions
1605
1637
1606 service should be a readily-identifiable subsystem, which will
1638 service should be a readily-identifiable subsystem, which will
1607 allow filtering.
1639 allow filtering.
1608
1640
1609 *msg should be a newline-terminated format string to log, and
1641 *msg should be a newline-terminated format string to log, and
1610 then any values to %-format into that format string.
1642 then any values to %-format into that format string.
1611
1643
1612 **opts currently has no defined meanings.
1644 **opts currently has no defined meanings.
1613 '''
1645 '''
1614
1646
1615 def label(self, msg, label):
1647 def label(self, msg, label):
1616 '''style msg based on supplied label
1648 '''style msg based on supplied label
1617
1649
1618 If some color mode is enabled, this will add the necessary control
1650 If some color mode is enabled, this will add the necessary control
1619 characters to apply such color. In addition, 'debug' color mode adds
1651 characters to apply such color. In addition, 'debug' color mode adds
1620 markup showing which label affects a piece of text.
1652 markup showing which label affects a piece of text.
1621
1653
1622 ui.write(s, 'label') is equivalent to
1654 ui.write(s, 'label') is equivalent to
1623 ui.write(ui.label(s, 'label')).
1655 ui.write(ui.label(s, 'label')).
1624 '''
1656 '''
1625 if self._colormode is not None:
1657 if self._colormode is not None:
1626 return color.colorlabel(self, msg, label)
1658 return color.colorlabel(self, msg, label)
1627 return msg
1659 return msg
1628
1660
1629 def develwarn(self, msg, stacklevel=1, config=None):
1661 def develwarn(self, msg, stacklevel=1, config=None):
1630 """issue a developer warning message
1662 """issue a developer warning message
1631
1663
1632 Use 'stacklevel' to report the offender some layers further up in the
1664 Use 'stacklevel' to report the offender some layers further up in the
1633 stack.
1665 stack.
1634 """
1666 """
1635 if not self.configbool('devel', 'all-warnings'):
1667 if not self.configbool('devel', 'all-warnings'):
1636 if config is None or not self.configbool('devel', config):
1668 if config is None or not self.configbool('devel', config):
1637 return
1669 return
1638 msg = 'devel-warn: ' + msg
1670 msg = 'devel-warn: ' + msg
1639 stacklevel += 1 # get in develwarn
1671 stacklevel += 1 # get in develwarn
1640 if self.tracebackflag:
1672 if self.tracebackflag:
1641 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1673 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1642 self.log('develwarn', '%s at:\n%s' %
1674 self.log('develwarn', '%s at:\n%s' %
1643 (msg, ''.join(util.getstackframes(stacklevel))))
1675 (msg, ''.join(util.getstackframes(stacklevel))))
1644 else:
1676 else:
1645 curframe = inspect.currentframe()
1677 curframe = inspect.currentframe()
1646 calframe = inspect.getouterframes(curframe, 2)
1678 calframe = inspect.getouterframes(curframe, 2)
1647 fname, lineno, fmsg = calframe[stacklevel][1:4]
1679 fname, lineno, fmsg = calframe[stacklevel][1:4]
1648 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1680 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1649 self.write_err('%s at: %s:%d (%s)\n'
1681 self.write_err('%s at: %s:%d (%s)\n'
1650 % (msg, fname, lineno, fmsg))
1682 % (msg, fname, lineno, fmsg))
1651 self.log('develwarn', '%s at: %s:%d (%s)\n',
1683 self.log('develwarn', '%s at: %s:%d (%s)\n',
1652 msg, fname, lineno, fmsg)
1684 msg, fname, lineno, fmsg)
1653 curframe = calframe = None # avoid cycles
1685 curframe = calframe = None # avoid cycles
1654
1686
1655 def deprecwarn(self, msg, version, stacklevel=2):
1687 def deprecwarn(self, msg, version, stacklevel=2):
1656 """issue a deprecation warning
1688 """issue a deprecation warning
1657
1689
1658 - msg: message explaining what is deprecated and how to upgrade,
1690 - msg: message explaining what is deprecated and how to upgrade,
1659 - version: last version where the API will be supported,
1691 - version: last version where the API will be supported,
1660 """
1692 """
1661 if not (self.configbool('devel', 'all-warnings')
1693 if not (self.configbool('devel', 'all-warnings')
1662 or self.configbool('devel', 'deprec-warn')):
1694 or self.configbool('devel', 'deprec-warn')):
1663 return
1695 return
1664 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1696 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1665 " update your code.)") % version
1697 " update your code.)") % version
1666 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1698 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1667
1699
1668 def exportableenviron(self):
1700 def exportableenviron(self):
1669 """The environment variables that are safe to export, e.g. through
1701 """The environment variables that are safe to export, e.g. through
1670 hgweb.
1702 hgweb.
1671 """
1703 """
1672 return self._exportableenviron
1704 return self._exportableenviron
1673
1705
1674 @contextlib.contextmanager
1706 @contextlib.contextmanager
1675 def configoverride(self, overrides, source=""):
1707 def configoverride(self, overrides, source=""):
1676 """Context manager for temporary config overrides
1708 """Context manager for temporary config overrides
1677 `overrides` must be a dict of the following structure:
1709 `overrides` must be a dict of the following structure:
1678 {(section, name) : value}"""
1710 {(section, name) : value}"""
1679 backups = {}
1711 backups = {}
1680 try:
1712 try:
1681 for (section, name), value in overrides.items():
1713 for (section, name), value in overrides.items():
1682 backups[(section, name)] = self.backupconfig(section, name)
1714 backups[(section, name)] = self.backupconfig(section, name)
1683 self.setconfig(section, name, value, source)
1715 self.setconfig(section, name, value, source)
1684 yield
1716 yield
1685 finally:
1717 finally:
1686 for __, backup in backups.items():
1718 for __, backup in backups.items():
1687 self.restoreconfig(backup)
1719 self.restoreconfig(backup)
1688 # just restoring ui.quiet config to the previous value is not enough
1720 # just restoring ui.quiet config to the previous value is not enough
1689 # as it does not update ui.quiet class member
1721 # as it does not update ui.quiet class member
1690 if ('ui', 'quiet') in overrides:
1722 if ('ui', 'quiet') in overrides:
1691 self.fixconfig(section='ui')
1723 self.fixconfig(section='ui')
1692
1724
1693 class paths(dict):
1725 class paths(dict):
1694 """Represents a collection of paths and their configs.
1726 """Represents a collection of paths and their configs.
1695
1727
1696 Data is initially derived from ui instances and the config files they have
1728 Data is initially derived from ui instances and the config files they have
1697 loaded.
1729 loaded.
1698 """
1730 """
1699 def __init__(self, ui):
1731 def __init__(self, ui):
1700 dict.__init__(self)
1732 dict.__init__(self)
1701
1733
1702 for name, loc in ui.configitems('paths', ignoresub=True):
1734 for name, loc in ui.configitems('paths', ignoresub=True):
1703 # No location is the same as not existing.
1735 # No location is the same as not existing.
1704 if not loc:
1736 if not loc:
1705 continue
1737 continue
1706 loc, sub = ui.configsuboptions('paths', name)
1738 loc, sub = ui.configsuboptions('paths', name)
1707 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1739 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1708
1740
1709 def getpath(self, name, default=None):
1741 def getpath(self, name, default=None):
1710 """Return a ``path`` from a string, falling back to default.
1742 """Return a ``path`` from a string, falling back to default.
1711
1743
1712 ``name`` can be a named path or locations. Locations are filesystem
1744 ``name`` can be a named path or locations. Locations are filesystem
1713 paths or URIs.
1745 paths or URIs.
1714
1746
1715 Returns None if ``name`` is not a registered path, a URI, or a local
1747 Returns None if ``name`` is not a registered path, a URI, or a local
1716 path to a repo.
1748 path to a repo.
1717 """
1749 """
1718 # Only fall back to default if no path was requested.
1750 # Only fall back to default if no path was requested.
1719 if name is None:
1751 if name is None:
1720 if not default:
1752 if not default:
1721 default = ()
1753 default = ()
1722 elif not isinstance(default, (tuple, list)):
1754 elif not isinstance(default, (tuple, list)):
1723 default = (default,)
1755 default = (default,)
1724 for k in default:
1756 for k in default:
1725 try:
1757 try:
1726 return self[k]
1758 return self[k]
1727 except KeyError:
1759 except KeyError:
1728 continue
1760 continue
1729 return None
1761 return None
1730
1762
1731 # Most likely empty string.
1763 # Most likely empty string.
1732 # This may need to raise in the future.
1764 # This may need to raise in the future.
1733 if not name:
1765 if not name:
1734 return None
1766 return None
1735
1767
1736 try:
1768 try:
1737 return self[name]
1769 return self[name]
1738 except KeyError:
1770 except KeyError:
1739 # Try to resolve as a local path or URI.
1771 # Try to resolve as a local path or URI.
1740 try:
1772 try:
1741 # We don't pass sub-options in, so no need to pass ui instance.
1773 # We don't pass sub-options in, so no need to pass ui instance.
1742 return path(None, None, rawloc=name)
1774 return path(None, None, rawloc=name)
1743 except ValueError:
1775 except ValueError:
1744 raise error.RepoError(_('repository %s does not exist') %
1776 raise error.RepoError(_('repository %s does not exist') %
1745 name)
1777 name)
1746
1778
1747 _pathsuboptions = {}
1779 _pathsuboptions = {}
1748
1780
1749 def pathsuboption(option, attr):
1781 def pathsuboption(option, attr):
1750 """Decorator used to declare a path sub-option.
1782 """Decorator used to declare a path sub-option.
1751
1783
1752 Arguments are the sub-option name and the attribute it should set on
1784 Arguments are the sub-option name and the attribute it should set on
1753 ``path`` instances.
1785 ``path`` instances.
1754
1786
1755 The decorated function will receive as arguments a ``ui`` instance,
1787 The decorated function will receive as arguments a ``ui`` instance,
1756 ``path`` instance, and the string value of this option from the config.
1788 ``path`` instance, and the string value of this option from the config.
1757 The function should return the value that will be set on the ``path``
1789 The function should return the value that will be set on the ``path``
1758 instance.
1790 instance.
1759
1791
1760 This decorator can be used to perform additional verification of
1792 This decorator can be used to perform additional verification of
1761 sub-options and to change the type of sub-options.
1793 sub-options and to change the type of sub-options.
1762 """
1794 """
1763 def register(func):
1795 def register(func):
1764 _pathsuboptions[option] = (attr, func)
1796 _pathsuboptions[option] = (attr, func)
1765 return func
1797 return func
1766 return register
1798 return register
1767
1799
1768 @pathsuboption('pushurl', 'pushloc')
1800 @pathsuboption('pushurl', 'pushloc')
1769 def pushurlpathoption(ui, path, value):
1801 def pushurlpathoption(ui, path, value):
1770 u = util.url(value)
1802 u = util.url(value)
1771 # Actually require a URL.
1803 # Actually require a URL.
1772 if not u.scheme:
1804 if not u.scheme:
1773 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1805 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1774 return None
1806 return None
1775
1807
1776 # Don't support the #foo syntax in the push URL to declare branch to
1808 # Don't support the #foo syntax in the push URL to declare branch to
1777 # push.
1809 # push.
1778 if u.fragment:
1810 if u.fragment:
1779 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1811 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1780 'ignoring)\n') % path.name)
1812 'ignoring)\n') % path.name)
1781 u.fragment = None
1813 u.fragment = None
1782
1814
1783 return bytes(u)
1815 return bytes(u)
1784
1816
1785 @pathsuboption('pushrev', 'pushrev')
1817 @pathsuboption('pushrev', 'pushrev')
1786 def pushrevpathoption(ui, path, value):
1818 def pushrevpathoption(ui, path, value):
1787 return value
1819 return value
1788
1820
1789 class path(object):
1821 class path(object):
1790 """Represents an individual path and its configuration."""
1822 """Represents an individual path and its configuration."""
1791
1823
1792 def __init__(self, ui, name, rawloc=None, suboptions=None):
1824 def __init__(self, ui, name, rawloc=None, suboptions=None):
1793 """Construct a path from its config options.
1825 """Construct a path from its config options.
1794
1826
1795 ``ui`` is the ``ui`` instance the path is coming from.
1827 ``ui`` is the ``ui`` instance the path is coming from.
1796 ``name`` is the symbolic name of the path.
1828 ``name`` is the symbolic name of the path.
1797 ``rawloc`` is the raw location, as defined in the config.
1829 ``rawloc`` is the raw location, as defined in the config.
1798 ``pushloc`` is the raw locations pushes should be made to.
1830 ``pushloc`` is the raw locations pushes should be made to.
1799
1831
1800 If ``name`` is not defined, we require that the location be a) a local
1832 If ``name`` is not defined, we require that the location be a) a local
1801 filesystem path with a .hg directory or b) a URL. If not,
1833 filesystem path with a .hg directory or b) a URL. If not,
1802 ``ValueError`` is raised.
1834 ``ValueError`` is raised.
1803 """
1835 """
1804 if not rawloc:
1836 if not rawloc:
1805 raise ValueError('rawloc must be defined')
1837 raise ValueError('rawloc must be defined')
1806
1838
1807 # Locations may define branches via syntax <base>#<branch>.
1839 # Locations may define branches via syntax <base>#<branch>.
1808 u = util.url(rawloc)
1840 u = util.url(rawloc)
1809 branch = None
1841 branch = None
1810 if u.fragment:
1842 if u.fragment:
1811 branch = u.fragment
1843 branch = u.fragment
1812 u.fragment = None
1844 u.fragment = None
1813
1845
1814 self.url = u
1846 self.url = u
1815 self.branch = branch
1847 self.branch = branch
1816
1848
1817 self.name = name
1849 self.name = name
1818 self.rawloc = rawloc
1850 self.rawloc = rawloc
1819 self.loc = '%s' % u
1851 self.loc = '%s' % u
1820
1852
1821 # When given a raw location but not a symbolic name, validate the
1853 # When given a raw location but not a symbolic name, validate the
1822 # location is valid.
1854 # location is valid.
1823 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1855 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1824 raise ValueError('location is not a URL or path to a local '
1856 raise ValueError('location is not a URL or path to a local '
1825 'repo: %s' % rawloc)
1857 'repo: %s' % rawloc)
1826
1858
1827 suboptions = suboptions or {}
1859 suboptions = suboptions or {}
1828
1860
1829 # Now process the sub-options. If a sub-option is registered, its
1861 # Now process the sub-options. If a sub-option is registered, its
1830 # attribute will always be present. The value will be None if there
1862 # attribute will always be present. The value will be None if there
1831 # was no valid sub-option.
1863 # was no valid sub-option.
1832 for suboption, (attr, func) in _pathsuboptions.iteritems():
1864 for suboption, (attr, func) in _pathsuboptions.iteritems():
1833 if suboption not in suboptions:
1865 if suboption not in suboptions:
1834 setattr(self, attr, None)
1866 setattr(self, attr, None)
1835 continue
1867 continue
1836
1868
1837 value = func(ui, self, suboptions[suboption])
1869 value = func(ui, self, suboptions[suboption])
1838 setattr(self, attr, value)
1870 setattr(self, attr, value)
1839
1871
1840 def _isvalidlocalpath(self, path):
1872 def _isvalidlocalpath(self, path):
1841 """Returns True if the given path is a potentially valid repository.
1873 """Returns True if the given path is a potentially valid repository.
1842 This is its own function so that extensions can change the definition of
1874 This is its own function so that extensions can change the definition of
1843 'valid' in this case (like when pulling from a git repo into a hg
1875 'valid' in this case (like when pulling from a git repo into a hg
1844 one)."""
1876 one)."""
1845 return os.path.isdir(os.path.join(path, '.hg'))
1877 return os.path.isdir(os.path.join(path, '.hg'))
1846
1878
1847 @property
1879 @property
1848 def suboptions(self):
1880 def suboptions(self):
1849 """Return sub-options and their values for this path.
1881 """Return sub-options and their values for this path.
1850
1882
1851 This is intended to be used for presentation purposes.
1883 This is intended to be used for presentation purposes.
1852 """
1884 """
1853 d = {}
1885 d = {}
1854 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1886 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1855 value = getattr(self, attr)
1887 value = getattr(self, attr)
1856 if value is not None:
1888 if value is not None:
1857 d[subopt] = value
1889 d[subopt] = value
1858 return d
1890 return d
1859
1891
1860 # we instantiate one globally shared progress bar to avoid
1892 # we instantiate one globally shared progress bar to avoid
1861 # competing progress bars when multiple UI objects get created
1893 # competing progress bars when multiple UI objects get created
1862 _progresssingleton = None
1894 _progresssingleton = None
1863
1895
1864 def getprogbar(ui):
1896 def getprogbar(ui):
1865 global _progresssingleton
1897 global _progresssingleton
1866 if _progresssingleton is None:
1898 if _progresssingleton is None:
1867 # passing 'ui' object to the singleton is fishy,
1899 # passing 'ui' object to the singleton is fishy,
1868 # this is how the extension used to work but feel free to rework it.
1900 # this is how the extension used to work but feel free to rework it.
1869 _progresssingleton = progress.progbar(ui)
1901 _progresssingleton = progress.progbar(ui)
1870 return _progresssingleton
1902 return _progresssingleton
1871
1903
1872 def haveprogbar():
1904 def haveprogbar():
1873 return _progresssingleton is not None
1905 return _progresssingleton is not None
@@ -1,417 +1,450 b''
1 # procutil.py - utility for managing processes and executable environment
1 # procutil.py - utility for managing processes and executable environment
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import contextlib
12 import contextlib
13 import imp
13 import imp
14 import io
14 import io
15 import os
15 import os
16 import signal
16 import signal
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import time
19 import time
20
20
21 from ..i18n import _
21 from ..i18n import _
22
22
23 from .. import (
23 from .. import (
24 encoding,
24 encoding,
25 error,
25 error,
26 policy,
26 policy,
27 pycompat,
27 pycompat,
28 )
28 )
29
29
30 osutil = policy.importmod(r'osutil')
30 osutil = policy.importmod(r'osutil')
31
31
32 stderr = pycompat.stderr
32 stderr = pycompat.stderr
33 stdin = pycompat.stdin
33 stdin = pycompat.stdin
34 stdout = pycompat.stdout
34 stdout = pycompat.stdout
35
35
36 def isatty(fp):
36 def isatty(fp):
37 try:
37 try:
38 return fp.isatty()
38 return fp.isatty()
39 except AttributeError:
39 except AttributeError:
40 return False
40 return False
41
41
42 # glibc determines buffering on first write to stdout - if we replace a TTY
42 # glibc determines buffering on first write to stdout - if we replace a TTY
43 # destined stdout with a pipe destined stdout (e.g. pager), we want line
43 # destined stdout with a pipe destined stdout (e.g. pager), we want line
44 # buffering (or unbuffered, on Windows)
44 # buffering (or unbuffered, on Windows)
45 if isatty(stdout):
45 if isatty(stdout):
46 if pycompat.iswindows:
46 if pycompat.iswindows:
47 # Windows doesn't support line buffering
47 # Windows doesn't support line buffering
48 stdout = os.fdopen(stdout.fileno(), r'wb', 0)
48 stdout = os.fdopen(stdout.fileno(), r'wb', 0)
49 else:
49 else:
50 stdout = os.fdopen(stdout.fileno(), r'wb', 1)
50 stdout = os.fdopen(stdout.fileno(), r'wb', 1)
51
51
52 if pycompat.iswindows:
52 if pycompat.iswindows:
53 from .. import windows as platform
53 from .. import windows as platform
54 stdout = platform.winstdout(stdout)
54 stdout = platform.winstdout(stdout)
55 else:
55 else:
56 from .. import posix as platform
56 from .. import posix as platform
57
57
58 findexe = platform.findexe
58 findexe = platform.findexe
59 _gethgcmd = platform.gethgcmd
59 _gethgcmd = platform.gethgcmd
60 getuser = platform.getuser
60 getuser = platform.getuser
61 getpid = os.getpid
61 getpid = os.getpid
62 hidewindow = platform.hidewindow
62 hidewindow = platform.hidewindow
63 quotecommand = platform.quotecommand
63 quotecommand = platform.quotecommand
64 readpipe = platform.readpipe
64 readpipe = platform.readpipe
65 setbinary = platform.setbinary
65 setbinary = platform.setbinary
66 setsignalhandler = platform.setsignalhandler
66 setsignalhandler = platform.setsignalhandler
67 shellquote = platform.shellquote
67 shellquote = platform.shellquote
68 shellsplit = platform.shellsplit
68 shellsplit = platform.shellsplit
69 spawndetached = platform.spawndetached
69 spawndetached = platform.spawndetached
70 sshargs = platform.sshargs
70 sshargs = platform.sshargs
71 testpid = platform.testpid
71 testpid = platform.testpid
72
72
73 try:
73 try:
74 setprocname = osutil.setprocname
74 setprocname = osutil.setprocname
75 except AttributeError:
75 except AttributeError:
76 pass
76 pass
77 try:
77 try:
78 unblocksignal = osutil.unblocksignal
78 unblocksignal = osutil.unblocksignal
79 except AttributeError:
79 except AttributeError:
80 pass
80 pass
81
81
82 closefds = pycompat.isposix
82 closefds = pycompat.isposix
83
83
84 def explainexit(code):
84 def explainexit(code):
85 """return a message describing a subprocess status
85 """return a message describing a subprocess status
86 (codes from kill are negative - not os.system/wait encoding)"""
86 (codes from kill are negative - not os.system/wait encoding)"""
87 if code >= 0:
87 if code >= 0:
88 return _("exited with status %d") % code
88 return _("exited with status %d") % code
89 return _("killed by signal %d") % -code
89 return _("killed by signal %d") % -code
90
90
91 class _pfile(object):
91 class _pfile(object):
92 """File-like wrapper for a stream opened by subprocess.Popen()"""
92 """File-like wrapper for a stream opened by subprocess.Popen()"""
93
93
94 def __init__(self, proc, fp):
94 def __init__(self, proc, fp):
95 self._proc = proc
95 self._proc = proc
96 self._fp = fp
96 self._fp = fp
97
97
98 def close(self):
98 def close(self):
99 # unlike os.popen(), this returns an integer in subprocess coding
99 # unlike os.popen(), this returns an integer in subprocess coding
100 self._fp.close()
100 self._fp.close()
101 return self._proc.wait()
101 return self._proc.wait()
102
102
103 def __iter__(self):
103 def __iter__(self):
104 return iter(self._fp)
104 return iter(self._fp)
105
105
106 def __getattr__(self, attr):
106 def __getattr__(self, attr):
107 return getattr(self._fp, attr)
107 return getattr(self._fp, attr)
108
108
109 def __enter__(self):
109 def __enter__(self):
110 return self
110 return self
111
111
112 def __exit__(self, exc_type, exc_value, exc_tb):
112 def __exit__(self, exc_type, exc_value, exc_tb):
113 self.close()
113 self.close()
114
114
115 def popen(cmd, mode='rb', bufsize=-1):
115 def popen(cmd, mode='rb', bufsize=-1):
116 if mode == 'rb':
116 if mode == 'rb':
117 return _popenreader(cmd, bufsize)
117 return _popenreader(cmd, bufsize)
118 elif mode == 'wb':
118 elif mode == 'wb':
119 return _popenwriter(cmd, bufsize)
119 return _popenwriter(cmd, bufsize)
120 raise error.ProgrammingError('unsupported mode: %r' % mode)
120 raise error.ProgrammingError('unsupported mode: %r' % mode)
121
121
122 def _popenreader(cmd, bufsize):
122 def _popenreader(cmd, bufsize):
123 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
123 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
124 close_fds=closefds,
124 close_fds=closefds,
125 stdout=subprocess.PIPE)
125 stdout=subprocess.PIPE)
126 return _pfile(p, p.stdout)
126 return _pfile(p, p.stdout)
127
127
128 def _popenwriter(cmd, bufsize):
128 def _popenwriter(cmd, bufsize):
129 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
129 p = subprocess.Popen(quotecommand(cmd), shell=True, bufsize=bufsize,
130 close_fds=closefds,
130 close_fds=closefds,
131 stdin=subprocess.PIPE)
131 stdin=subprocess.PIPE)
132 return _pfile(p, p.stdin)
132 return _pfile(p, p.stdin)
133
133
134 def popen2(cmd, env=None):
134 def popen2(cmd, env=None):
135 # Setting bufsize to -1 lets the system decide the buffer size.
135 # Setting bufsize to -1 lets the system decide the buffer size.
136 # The default for bufsize is 0, meaning unbuffered. This leads to
136 # The default for bufsize is 0, meaning unbuffered. This leads to
137 # poor performance on Mac OS X: http://bugs.python.org/issue4194
137 # poor performance on Mac OS X: http://bugs.python.org/issue4194
138 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
138 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
139 close_fds=closefds,
139 close_fds=closefds,
140 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
140 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
141 env=env)
141 env=env)
142 return p.stdin, p.stdout
142 return p.stdin, p.stdout
143
143
144 def popen3(cmd, env=None):
144 def popen3(cmd, env=None):
145 stdin, stdout, stderr, p = popen4(cmd, env)
145 stdin, stdout, stderr, p = popen4(cmd, env)
146 return stdin, stdout, stderr
146 return stdin, stdout, stderr
147
147
148 def popen4(cmd, env=None, bufsize=-1):
148 def popen4(cmd, env=None, bufsize=-1):
149 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
149 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
150 close_fds=closefds,
150 close_fds=closefds,
151 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
151 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
152 stderr=subprocess.PIPE,
152 stderr=subprocess.PIPE,
153 env=env)
153 env=env)
154 return p.stdin, p.stdout, p.stderr, p
154 return p.stdin, p.stdout, p.stderr, p
155
155
156 def pipefilter(s, cmd):
156 def pipefilter(s, cmd):
157 '''filter string S through command CMD, returning its output'''
157 '''filter string S through command CMD, returning its output'''
158 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
158 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
159 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
159 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
160 pout, perr = p.communicate(s)
160 pout, perr = p.communicate(s)
161 return pout
161 return pout
162
162
163 def tempfilter(s, cmd):
163 def tempfilter(s, cmd):
164 '''filter string S through a pair of temporary files with CMD.
164 '''filter string S through a pair of temporary files with CMD.
165 CMD is used as a template to create the real command to be run,
165 CMD is used as a template to create the real command to be run,
166 with the strings INFILE and OUTFILE replaced by the real names of
166 with the strings INFILE and OUTFILE replaced by the real names of
167 the temporary files generated.'''
167 the temporary files generated.'''
168 inname, outname = None, None
168 inname, outname = None, None
169 try:
169 try:
170 infd, inname = pycompat.mkstemp(prefix='hg-filter-in-')
170 infd, inname = pycompat.mkstemp(prefix='hg-filter-in-')
171 fp = os.fdopen(infd, r'wb')
171 fp = os.fdopen(infd, r'wb')
172 fp.write(s)
172 fp.write(s)
173 fp.close()
173 fp.close()
174 outfd, outname = pycompat.mkstemp(prefix='hg-filter-out-')
174 outfd, outname = pycompat.mkstemp(prefix='hg-filter-out-')
175 os.close(outfd)
175 os.close(outfd)
176 cmd = cmd.replace('INFILE', inname)
176 cmd = cmd.replace('INFILE', inname)
177 cmd = cmd.replace('OUTFILE', outname)
177 cmd = cmd.replace('OUTFILE', outname)
178 code = system(cmd)
178 code = system(cmd)
179 if pycompat.sysplatform == 'OpenVMS' and code & 1:
179 if pycompat.sysplatform == 'OpenVMS' and code & 1:
180 code = 0
180 code = 0
181 if code:
181 if code:
182 raise error.Abort(_("command '%s' failed: %s") %
182 raise error.Abort(_("command '%s' failed: %s") %
183 (cmd, explainexit(code)))
183 (cmd, explainexit(code)))
184 with open(outname, 'rb') as fp:
184 with open(outname, 'rb') as fp:
185 return fp.read()
185 return fp.read()
186 finally:
186 finally:
187 try:
187 try:
188 if inname:
188 if inname:
189 os.unlink(inname)
189 os.unlink(inname)
190 except OSError:
190 except OSError:
191 pass
191 pass
192 try:
192 try:
193 if outname:
193 if outname:
194 os.unlink(outname)
194 os.unlink(outname)
195 except OSError:
195 except OSError:
196 pass
196 pass
197
197
198 _filtertable = {
198 _filtertable = {
199 'tempfile:': tempfilter,
199 'tempfile:': tempfilter,
200 'pipe:': pipefilter,
200 'pipe:': pipefilter,
201 }
201 }
202
202
203 def filter(s, cmd):
203 def filter(s, cmd):
204 "filter a string through a command that transforms its input to its output"
204 "filter a string through a command that transforms its input to its output"
205 for name, fn in _filtertable.iteritems():
205 for name, fn in _filtertable.iteritems():
206 if cmd.startswith(name):
206 if cmd.startswith(name):
207 return fn(s, cmd[len(name):].lstrip())
207 return fn(s, cmd[len(name):].lstrip())
208 return pipefilter(s, cmd)
208 return pipefilter(s, cmd)
209
209
210 def mainfrozen():
210 def mainfrozen():
211 """return True if we are a frozen executable.
211 """return True if we are a frozen executable.
212
212
213 The code supports py2exe (most common, Windows only) and tools/freeze
213 The code supports py2exe (most common, Windows only) and tools/freeze
214 (portable, not much used).
214 (portable, not much used).
215 """
215 """
216 return (pycompat.safehasattr(sys, "frozen") or # new py2exe
216 return (pycompat.safehasattr(sys, "frozen") or # new py2exe
217 pycompat.safehasattr(sys, "importers") or # old py2exe
217 pycompat.safehasattr(sys, "importers") or # old py2exe
218 imp.is_frozen(u"__main__")) # tools/freeze
218 imp.is_frozen(u"__main__")) # tools/freeze
219
219
220 _hgexecutable = None
220 _hgexecutable = None
221
221
222 def hgexecutable():
222 def hgexecutable():
223 """return location of the 'hg' executable.
223 """return location of the 'hg' executable.
224
224
225 Defaults to $HG or 'hg' in the search path.
225 Defaults to $HG or 'hg' in the search path.
226 """
226 """
227 if _hgexecutable is None:
227 if _hgexecutable is None:
228 hg = encoding.environ.get('HG')
228 hg = encoding.environ.get('HG')
229 mainmod = sys.modules[r'__main__']
229 mainmod = sys.modules[r'__main__']
230 if hg:
230 if hg:
231 _sethgexecutable(hg)
231 _sethgexecutable(hg)
232 elif mainfrozen():
232 elif mainfrozen():
233 if getattr(sys, 'frozen', None) == 'macosx_app':
233 if getattr(sys, 'frozen', None) == 'macosx_app':
234 # Env variable set by py2app
234 # Env variable set by py2app
235 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
235 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
236 else:
236 else:
237 _sethgexecutable(pycompat.sysexecutable)
237 _sethgexecutable(pycompat.sysexecutable)
238 elif (os.path.basename(
238 elif (os.path.basename(
239 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
239 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
240 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
240 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
241 else:
241 else:
242 exe = findexe('hg') or os.path.basename(sys.argv[0])
242 exe = findexe('hg') or os.path.basename(sys.argv[0])
243 _sethgexecutable(exe)
243 _sethgexecutable(exe)
244 return _hgexecutable
244 return _hgexecutable
245
245
246 def _sethgexecutable(path):
246 def _sethgexecutable(path):
247 """set location of the 'hg' executable"""
247 """set location of the 'hg' executable"""
248 global _hgexecutable
248 global _hgexecutable
249 _hgexecutable = path
249 _hgexecutable = path
250
250
251 def _testfileno(f, stdf):
251 def _testfileno(f, stdf):
252 fileno = getattr(f, 'fileno', None)
252 fileno = getattr(f, 'fileno', None)
253 try:
253 try:
254 return fileno and fileno() == stdf.fileno()
254 return fileno and fileno() == stdf.fileno()
255 except io.UnsupportedOperation:
255 except io.UnsupportedOperation:
256 return False # fileno() raised UnsupportedOperation
256 return False # fileno() raised UnsupportedOperation
257
257
258 def isstdin(f):
258 def isstdin(f):
259 return _testfileno(f, sys.__stdin__)
259 return _testfileno(f, sys.__stdin__)
260
260
261 def isstdout(f):
261 def isstdout(f):
262 return _testfileno(f, sys.__stdout__)
262 return _testfileno(f, sys.__stdout__)
263
263
264 def protectstdio(uin, uout):
264 def protectstdio(uin, uout):
265 """Duplicate streams and redirect original if (uin, uout) are stdio
265 """Duplicate streams and redirect original if (uin, uout) are stdio
266
266
267 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
267 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
268 redirected to stderr so the output is still readable.
268 redirected to stderr so the output is still readable.
269
269
270 Returns (fin, fout) which point to the original (uin, uout) fds, but
270 Returns (fin, fout) which point to the original (uin, uout) fds, but
271 may be copy of (uin, uout). The returned streams can be considered
271 may be copy of (uin, uout). The returned streams can be considered
272 "owned" in that print(), exec(), etc. never reach to them.
272 "owned" in that print(), exec(), etc. never reach to them.
273 """
273 """
274 uout.flush()
274 uout.flush()
275 fin, fout = uin, uout
275 fin, fout = uin, uout
276 if uin is stdin:
276 if uin is stdin:
277 newfd = os.dup(uin.fileno())
277 newfd = os.dup(uin.fileno())
278 nullfd = os.open(os.devnull, os.O_RDONLY)
278 nullfd = os.open(os.devnull, os.O_RDONLY)
279 os.dup2(nullfd, uin.fileno())
279 os.dup2(nullfd, uin.fileno())
280 os.close(nullfd)
280 os.close(nullfd)
281 fin = os.fdopen(newfd, r'rb')
281 fin = os.fdopen(newfd, r'rb')
282 if uout is stdout:
282 if uout is stdout:
283 newfd = os.dup(uout.fileno())
283 newfd = os.dup(uout.fileno())
284 os.dup2(stderr.fileno(), uout.fileno())
284 os.dup2(stderr.fileno(), uout.fileno())
285 fout = os.fdopen(newfd, r'wb')
285 fout = os.fdopen(newfd, r'wb')
286 return fin, fout
286 return fin, fout
287
287
288 def restorestdio(uin, uout, fin, fout):
288 def restorestdio(uin, uout, fin, fout):
289 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
289 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
290 uout.flush()
290 uout.flush()
291 for f, uif in [(fin, uin), (fout, uout)]:
291 for f, uif in [(fin, uin), (fout, uout)]:
292 if f is not uif:
292 if f is not uif:
293 os.dup2(f.fileno(), uif.fileno())
293 os.dup2(f.fileno(), uif.fileno())
294 f.close()
294 f.close()
295
295
296 @contextlib.contextmanager
296 @contextlib.contextmanager
297 def protectedstdio(uin, uout):
297 def protectedstdio(uin, uout):
298 """Run code block with protected standard streams"""
298 """Run code block with protected standard streams"""
299 fin, fout = protectstdio(uin, uout)
299 fin, fout = protectstdio(uin, uout)
300 try:
300 try:
301 yield fin, fout
301 yield fin, fout
302 finally:
302 finally:
303 restorestdio(uin, uout, fin, fout)
303 restorestdio(uin, uout, fin, fout)
304
304
305 def shellenviron(environ=None):
305 def shellenviron(environ=None):
306 """return environ with optional override, useful for shelling out"""
306 """return environ with optional override, useful for shelling out"""
307 def py2shell(val):
307 def py2shell(val):
308 'convert python object into string that is useful to shell'
308 'convert python object into string that is useful to shell'
309 if val is None or val is False:
309 if val is None or val is False:
310 return '0'
310 return '0'
311 if val is True:
311 if val is True:
312 return '1'
312 return '1'
313 return pycompat.bytestr(val)
313 return pycompat.bytestr(val)
314 env = dict(encoding.environ)
314 env = dict(encoding.environ)
315 if environ:
315 if environ:
316 env.update((k, py2shell(v)) for k, v in environ.iteritems())
316 env.update((k, py2shell(v)) for k, v in environ.iteritems())
317 env['HG'] = hgexecutable()
317 env['HG'] = hgexecutable()
318 return env
318 return env
319
319
320 if pycompat.iswindows:
320 if pycompat.iswindows:
321 def shelltonative(cmd, env):
321 def shelltonative(cmd, env):
322 return platform.shelltocmdexe(cmd, shellenviron(env))
322 return platform.shelltocmdexe(cmd, shellenviron(env))
323 else:
323 else:
324 def shelltonative(cmd, env):
324 def shelltonative(cmd, env):
325 return cmd
325 return cmd
326
326
327 def system(cmd, environ=None, cwd=None, out=None):
327 def system(cmd, environ=None, cwd=None, out=None):
328 '''enhanced shell command execution.
328 '''enhanced shell command execution.
329 run with environment maybe modified, maybe in different dir.
329 run with environment maybe modified, maybe in different dir.
330
330
331 if out is specified, it is assumed to be a file-like object that has a
331 if out is specified, it is assumed to be a file-like object that has a
332 write() method. stdout and stderr will be redirected to out.'''
332 write() method. stdout and stderr will be redirected to out.'''
333 try:
333 try:
334 stdout.flush()
334 stdout.flush()
335 except Exception:
335 except Exception:
336 pass
336 pass
337 cmd = quotecommand(cmd)
337 cmd = quotecommand(cmd)
338 env = shellenviron(environ)
338 env = shellenviron(environ)
339 if out is None or isstdout(out):
339 if out is None or isstdout(out):
340 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
340 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
341 env=env, cwd=cwd)
341 env=env, cwd=cwd)
342 else:
342 else:
343 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
343 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
344 env=env, cwd=cwd, stdout=subprocess.PIPE,
344 env=env, cwd=cwd, stdout=subprocess.PIPE,
345 stderr=subprocess.STDOUT)
345 stderr=subprocess.STDOUT)
346 for line in iter(proc.stdout.readline, ''):
346 for line in iter(proc.stdout.readline, ''):
347 out.write(line)
347 out.write(line)
348 proc.wait()
348 proc.wait()
349 rc = proc.returncode
349 rc = proc.returncode
350 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
350 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
351 rc = 0
351 rc = 0
352 return rc
352 return rc
353
353
354 def gui():
354 def gui():
355 '''Are we running in a GUI?'''
355 '''Are we running in a GUI?'''
356 if pycompat.isdarwin:
356 if pycompat.isdarwin:
357 if 'SSH_CONNECTION' in encoding.environ:
357 if 'SSH_CONNECTION' in encoding.environ:
358 # handle SSH access to a box where the user is logged in
358 # handle SSH access to a box where the user is logged in
359 return False
359 return False
360 elif getattr(osutil, 'isgui', None):
360 elif getattr(osutil, 'isgui', None):
361 # check if a CoreGraphics session is available
361 # check if a CoreGraphics session is available
362 return osutil.isgui()
362 return osutil.isgui()
363 else:
363 else:
364 # pure build; use a safe default
364 # pure build; use a safe default
365 return True
365 return True
366 else:
366 else:
367 return pycompat.iswindows or encoding.environ.get("DISPLAY")
367 return pycompat.iswindows or encoding.environ.get("DISPLAY")
368
368
369 def hgcmd():
369 def hgcmd():
370 """Return the command used to execute current hg
370 """Return the command used to execute current hg
371
371
372 This is different from hgexecutable() because on Windows we want
372 This is different from hgexecutable() because on Windows we want
373 to avoid things opening new shell windows like batch files, so we
373 to avoid things opening new shell windows like batch files, so we
374 get either the python call or current executable.
374 get either the python call or current executable.
375 """
375 """
376 if mainfrozen():
376 if mainfrozen():
377 if getattr(sys, 'frozen', None) == 'macosx_app':
377 if getattr(sys, 'frozen', None) == 'macosx_app':
378 # Env variable set by py2app
378 # Env variable set by py2app
379 return [encoding.environ['EXECUTABLEPATH']]
379 return [encoding.environ['EXECUTABLEPATH']]
380 else:
380 else:
381 return [pycompat.sysexecutable]
381 return [pycompat.sysexecutable]
382 return _gethgcmd()
382 return _gethgcmd()
383
383
384 def rundetached(args, condfn):
384 def rundetached(args, condfn):
385 """Execute the argument list in a detached process.
385 """Execute the argument list in a detached process.
386
386
387 condfn is a callable which is called repeatedly and should return
387 condfn is a callable which is called repeatedly and should return
388 True once the child process is known to have started successfully.
388 True once the child process is known to have started successfully.
389 At this point, the child process PID is returned. If the child
389 At this point, the child process PID is returned. If the child
390 process fails to start or finishes before condfn() evaluates to
390 process fails to start or finishes before condfn() evaluates to
391 True, return -1.
391 True, return -1.
392 """
392 """
393 # Windows case is easier because the child process is either
393 # Windows case is easier because the child process is either
394 # successfully starting and validating the condition or exiting
394 # successfully starting and validating the condition or exiting
395 # on failure. We just poll on its PID. On Unix, if the child
395 # on failure. We just poll on its PID. On Unix, if the child
396 # process fails to start, it will be left in a zombie state until
396 # process fails to start, it will be left in a zombie state until
397 # the parent wait on it, which we cannot do since we expect a long
397 # the parent wait on it, which we cannot do since we expect a long
398 # running process on success. Instead we listen for SIGCHLD telling
398 # running process on success. Instead we listen for SIGCHLD telling
399 # us our child process terminated.
399 # us our child process terminated.
400 terminated = set()
400 terminated = set()
401 def handler(signum, frame):
401 def handler(signum, frame):
402 terminated.add(os.wait())
402 terminated.add(os.wait())
403 prevhandler = None
403 prevhandler = None
404 SIGCHLD = getattr(signal, 'SIGCHLD', None)
404 SIGCHLD = getattr(signal, 'SIGCHLD', None)
405 if SIGCHLD is not None:
405 if SIGCHLD is not None:
406 prevhandler = signal.signal(SIGCHLD, handler)
406 prevhandler = signal.signal(SIGCHLD, handler)
407 try:
407 try:
408 pid = spawndetached(args)
408 pid = spawndetached(args)
409 while not condfn():
409 while not condfn():
410 if ((pid in terminated or not testpid(pid))
410 if ((pid in terminated or not testpid(pid))
411 and not condfn()):
411 and not condfn()):
412 return -1
412 return -1
413 time.sleep(0.1)
413 time.sleep(0.1)
414 return pid
414 return pid
415 finally:
415 finally:
416 if prevhandler is not None:
416 if prevhandler is not None:
417 signal.signal(signal.SIGCHLD, prevhandler)
417 signal.signal(signal.SIGCHLD, prevhandler)
418
419 @contextlib.contextmanager
420 def uninterruptable(warn):
421 """Inhibit SIGINT handling on a region of code.
422
423 Note that if this is called in a non-main thread, it turns into a no-op.
424
425 Args:
426 warn: A callable which takes no arguments, and returns True if the
427 previous signal handling should be restored.
428 """
429
430 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
431 shouldbail = []
432
433 def disabledsiginthandler(*args):
434 if warn():
435 signal.signal(signal.SIGINT, oldsiginthandler[0])
436 del oldsiginthandler[0]
437 shouldbail.append(True)
438
439 try:
440 try:
441 signal.signal(signal.SIGINT, disabledsiginthandler)
442 except ValueError:
443 # wrong thread, oh well, we tried
444 del oldsiginthandler[0]
445 yield
446 finally:
447 if oldsiginthandler:
448 signal.signal(signal.SIGINT, oldsiginthandler[0])
449 if shouldbail:
450 raise KeyboardInterrupt
General Comments 0
You need to be logged in to leave comments. Login now