##// END OF EJS Templates
config: track the "level" of a value...
marmoute -
r47367:a3dced4b default
parent child Browse files
Show More
@@ -1,325 +1,350 b''
1 # config.py - configuration parsing for Mercurial
1 # config.py - configuration parsing for Mercurial
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
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 errno
10 import errno
11 import os
11 import os
12
12
13 from .i18n import _
13 from .i18n import _
14 from .pycompat import getattr
14 from .pycompat import getattr
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22
22
23 class config(object):
23 class config(object):
24 def __init__(self, data=None):
24 def __init__(self, data=None):
25 self._current_source_level = 0
25 self._data = {}
26 self._data = {}
26 self._unset = []
27 self._unset = []
27 if data:
28 if data:
28 for k in data._data:
29 for k in data._data:
29 self._data[k] = data[k].copy()
30 self._data[k] = data[k].copy()
31 self._current_source_level = data._current_source_level + 1
32
33 def new_source(self):
34 """increment the source counter
35
36 This is used to define source priority when reading"""
37 self._current_source_level += 1
30
38
31 def copy(self):
39 def copy(self):
32 return config(self)
40 return config(self)
33
41
34 def __contains__(self, section):
42 def __contains__(self, section):
35 return section in self._data
43 return section in self._data
36
44
37 def hasitem(self, section, item):
45 def hasitem(self, section, item):
38 return item in self._data.get(section, {})
46 return item in self._data.get(section, {})
39
47
40 def __getitem__(self, section):
48 def __getitem__(self, section):
41 return self._data.get(section, {})
49 return self._data.get(section, {})
42
50
43 def __iter__(self):
51 def __iter__(self):
44 for d in self.sections():
52 for d in self.sections():
45 yield d
53 yield d
46
54
47 def update(self, src):
55 def update(self, src):
56 current_level = self._current_source_level
57 current_level += 1
58 max_level = self._current_source_level
48 for s, n in src._unset:
59 for s, n in src._unset:
49 ds = self._data.get(s, None)
60 ds = self._data.get(s, None)
50 if ds is not None and n in ds:
61 if ds is not None and n in ds:
51 self._data[s] = ds.preparewrite()
62 self._data[s] = ds.preparewrite()
52 del self._data[s][n]
63 del self._data[s][n]
53 for s in src:
64 for s in src:
54 ds = self._data.get(s, None)
65 ds = self._data.get(s, None)
55 if ds:
66 if ds:
56 self._data[s] = ds.preparewrite()
67 self._data[s] = ds.preparewrite()
57 else:
68 else:
58 self._data[s] = util.cowsortdict()
69 self._data[s] = util.cowsortdict()
59 self._data[s].update(src._data[s])
70 for k, v in src._data[s].items():
71 value, source, level = v
72 level += current_level
73 max_level = max(level, current_level)
74 self._data[s][k] = (value, source, level)
75 self._current_source_level = max_level
60
76
61 def _get(self, section, item):
77 def _get(self, section, item):
62 return self._data.get(section, {}).get(item)
78 return self._data.get(section, {}).get(item)
63
79
64 def get(self, section, item, default=None):
80 def get(self, section, item, default=None):
65 result = self._get(section, item)
81 result = self._get(section, item)
66 if result is None:
82 if result is None:
67 return default
83 return default
68 return result[0]
84 return result[0]
69
85
70 def backup(self, section, key):
86 def backup(self, section, key):
71 """return a tuple allowing restore to reinstall a previous value
87 """return a tuple allowing restore to reinstall a previous value
72
88
73 The main reason we need it is because it handles the "no data" case.
89 The main reason we need it is because it handles the "no data" case.
74 """
90 """
75 try:
91 try:
76 item = self._data[section][key]
92 item = self._data[section][key]
77 except KeyError:
93 except KeyError:
78 return (section, key)
94 return (section, key)
79 else:
95 else:
80 return (section, key) + item
96 return (section, key) + item
81
97
82 def source(self, section, item):
98 def source(self, section, item):
83 result = self._get(section, item)
99 result = self._get(section, item)
84 if result is None:
100 if result is None:
85 return b""
101 return b""
86 return result[1]
102 return result[1]
87
103
104 def level(self, section, item):
105 result = self._get(section, item)
106 if result is None:
107 return None
108 return result[2]
109
88 def sections(self):
110 def sections(self):
89 return sorted(self._data.keys())
111 return sorted(self._data.keys())
90
112
91 def items(self, section):
113 def items(self, section):
92 items = pycompat.iteritems(self._data.get(section, {}))
114 items = pycompat.iteritems(self._data.get(section, {}))
93 return [(k, v) for (k, (v, s)) in items]
115 return [(k, v[0]) for (k, v) in items]
94
116
95 def set(self, section, item, value, source=b""):
117 def set(self, section, item, value, source=b""):
96 if pycompat.ispy3:
118 if pycompat.ispy3:
97 assert not isinstance(
119 assert not isinstance(
98 section, str
120 section, str
99 ), b'config section may not be unicode strings on Python 3'
121 ), b'config section may not be unicode strings on Python 3'
100 assert not isinstance(
122 assert not isinstance(
101 item, str
123 item, str
102 ), b'config item may not be unicode strings on Python 3'
124 ), b'config item may not be unicode strings on Python 3'
103 assert not isinstance(
125 assert not isinstance(
104 value, str
126 value, str
105 ), b'config values may not be unicode strings on Python 3'
127 ), b'config values may not be unicode strings on Python 3'
106 if section not in self:
128 if section not in self:
107 self._data[section] = util.cowsortdict()
129 self._data[section] = util.cowsortdict()
108 else:
130 else:
109 self._data[section] = self._data[section].preparewrite()
131 self._data[section] = self._data[section].preparewrite()
110 self._data[section][item] = (value, source)
132 self._data[section][item] = (value, source, self._current_source_level)
111
133
112 def alter(self, section, key, new_value):
134 def alter(self, section, key, new_value):
113 """alter a value without altering its source or level
135 """alter a value without altering its source or level
114
136
115 This method is meant to be used by `ui.fixconfig` only."""
137 This method is meant to be used by `ui.fixconfig` only."""
116 item = self._data[section][key]
138 item = self._data[section][key]
117 size = len(item)
139 size = len(item)
118 new_item = (new_value,) + item[1:]
140 new_item = (new_value,) + item[1:]
119 assert len(new_item) == size
141 assert len(new_item) == size
120 self._data[section][key] = new_item
142 self._data[section][key] = new_item
121
143
122 def restore(self, data):
144 def restore(self, data):
123 """restore data returned by self.backup"""
145 """restore data returned by self.backup"""
124 if len(data) != 2:
146 if len(data) != 2:
125 # restore old data
147 # restore old data
126 section, key = data[:2]
148 section, key = data[:2]
127 item = data[2:]
149 item = data[2:]
128 self._data[section] = self._data[section].preparewrite()
150 self._data[section] = self._data[section].preparewrite()
129 self._data[section][key] = item
151 self._data[section][key] = item
130 else:
152 else:
131 # no data before, remove everything
153 # no data before, remove everything
132 section, item = data
154 section, item = data
133 if section in self._data:
155 if section in self._data:
134 self._data[section].pop(item, None)
156 self._data[section].pop(item, None)
135
157
136 def parse(self, src, data, sections=None, remap=None, include=None):
158 def parse(self, src, data, sections=None, remap=None, include=None):
137 sectionre = util.re.compile(br'\[([^\[]+)\]')
159 sectionre = util.re.compile(br'\[([^\[]+)\]')
138 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
160 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
139 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
161 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
140 emptyre = util.re.compile(br'(;|#|\s*$)')
162 emptyre = util.re.compile(br'(;|#|\s*$)')
141 commentre = util.re.compile(br'(;|#)')
163 commentre = util.re.compile(br'(;|#)')
142 unsetre = util.re.compile(br'%unset\s+(\S+)')
164 unsetre = util.re.compile(br'%unset\s+(\S+)')
143 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
165 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
144 section = b""
166 section = b""
145 item = None
167 item = None
146 line = 0
168 line = 0
147 cont = False
169 cont = False
148
170
149 if remap:
171 if remap:
150 section = remap.get(section, section)
172 section = remap.get(section, section)
151
173
152 for l in data.splitlines(True):
174 for l in data.splitlines(True):
153 line += 1
175 line += 1
154 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
176 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
155 # Someone set us up the BOM
177 # Someone set us up the BOM
156 l = l[3:]
178 l = l[3:]
157 if cont:
179 if cont:
158 if commentre.match(l):
180 if commentre.match(l):
159 continue
181 continue
160 m = contre.match(l)
182 m = contre.match(l)
161 if m:
183 if m:
162 if sections and section not in sections:
184 if sections and section not in sections:
163 continue
185 continue
164 v = self.get(section, item) + b"\n" + m.group(1)
186 v = self.get(section, item) + b"\n" + m.group(1)
165 self.set(section, item, v, b"%s:%d" % (src, line))
187 self.set(section, item, v, b"%s:%d" % (src, line))
166 continue
188 continue
167 item = None
189 item = None
168 cont = False
190 cont = False
169 m = includere.match(l)
191 m = includere.match(l)
170
192
171 if m and include:
193 if m and include:
172 expanded = util.expandpath(m.group(1))
194 expanded = util.expandpath(m.group(1))
173 try:
195 try:
174 include(expanded, remap=remap, sections=sections)
196 include(expanded, remap=remap, sections=sections)
175 except IOError as inst:
197 except IOError as inst:
176 if inst.errno != errno.ENOENT:
198 if inst.errno != errno.ENOENT:
177 raise error.ConfigError(
199 raise error.ConfigError(
178 _(b"cannot include %s (%s)")
200 _(b"cannot include %s (%s)")
179 % (expanded, encoding.strtolocal(inst.strerror)),
201 % (expanded, encoding.strtolocal(inst.strerror)),
180 b"%s:%d" % (src, line),
202 b"%s:%d" % (src, line),
181 )
203 )
182 continue
204 continue
183 if emptyre.match(l):
205 if emptyre.match(l):
184 continue
206 continue
185 m = sectionre.match(l)
207 m = sectionre.match(l)
186 if m:
208 if m:
187 section = m.group(1)
209 section = m.group(1)
188 if remap:
210 if remap:
189 section = remap.get(section, section)
211 section = remap.get(section, section)
190 if section not in self:
212 if section not in self:
191 self._data[section] = util.cowsortdict()
213 self._data[section] = util.cowsortdict()
192 continue
214 continue
193 m = itemre.match(l)
215 m = itemre.match(l)
194 if m:
216 if m:
195 item = m.group(1)
217 item = m.group(1)
196 cont = True
218 cont = True
197 if sections and section not in sections:
219 if sections and section not in sections:
198 continue
220 continue
199 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
221 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
200 continue
222 continue
201 m = unsetre.match(l)
223 m = unsetre.match(l)
202 if m:
224 if m:
203 name = m.group(1)
225 name = m.group(1)
204 if sections and section not in sections:
226 if sections and section not in sections:
205 continue
227 continue
206 if self.get(section, name) is not None:
228 if self.get(section, name) is not None:
207 self._data[section] = self._data[section].preparewrite()
229 self._data[section] = self._data[section].preparewrite()
208 del self._data[section][name]
230 del self._data[section][name]
209 self._unset.append((section, name))
231 self._unset.append((section, name))
210 continue
232 continue
211
233
212 message = l.rstrip()
234 message = l.rstrip()
213 if l.startswith(b' '):
235 if l.startswith(b' '):
214 message = b"unexpected leading whitespace: %s" % message
236 message = b"unexpected leading whitespace: %s" % message
215 raise error.ConfigError(message, (b"%s:%d" % (src, line)))
237 raise error.ConfigError(message, (b"%s:%d" % (src, line)))
216
238
217 def read(self, path, fp=None, sections=None, remap=None):
239 def read(self, path, fp=None, sections=None, remap=None):
240 self.new_source()
218 if not fp:
241 if not fp:
219 fp = util.posixfile(path, b'rb')
242 fp = util.posixfile(path, b'rb')
220 assert (
243 assert (
221 getattr(fp, 'mode', 'rb') == 'rb'
244 getattr(fp, 'mode', 'rb') == 'rb'
222 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
245 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
223 fp,
246 fp,
224 fp.mode,
247 fp.mode,
225 )
248 )
226
249
227 dir = os.path.dirname(path)
250 dir = os.path.dirname(path)
228
251
229 def include(rel, remap, sections):
252 def include(rel, remap, sections):
230 abs = os.path.normpath(os.path.join(dir, rel))
253 abs = os.path.normpath(os.path.join(dir, rel))
231 self.read(abs, remap=remap, sections=sections)
254 self.read(abs, remap=remap, sections=sections)
255 # anything after the include has a higher level
256 self.new_source()
232
257
233 self.parse(
258 self.parse(
234 path, fp.read(), sections=sections, remap=remap, include=include
259 path, fp.read(), sections=sections, remap=remap, include=include
235 )
260 )
236
261
237
262
238 def parselist(value):
263 def parselist(value):
239 """parse a configuration value as a list of comma/space separated strings
264 """parse a configuration value as a list of comma/space separated strings
240
265
241 >>> parselist(b'this,is "a small" ,test')
266 >>> parselist(b'this,is "a small" ,test')
242 ['this', 'is', 'a small', 'test']
267 ['this', 'is', 'a small', 'test']
243 """
268 """
244
269
245 def _parse_plain(parts, s, offset):
270 def _parse_plain(parts, s, offset):
246 whitespace = False
271 whitespace = False
247 while offset < len(s) and (
272 while offset < len(s) and (
248 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
273 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
249 ):
274 ):
250 whitespace = True
275 whitespace = True
251 offset += 1
276 offset += 1
252 if offset >= len(s):
277 if offset >= len(s):
253 return None, parts, offset
278 return None, parts, offset
254 if whitespace:
279 if whitespace:
255 parts.append(b'')
280 parts.append(b'')
256 if s[offset : offset + 1] == b'"' and not parts[-1]:
281 if s[offset : offset + 1] == b'"' and not parts[-1]:
257 return _parse_quote, parts, offset + 1
282 return _parse_quote, parts, offset + 1
258 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
283 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
259 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
284 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
260 return _parse_plain, parts, offset + 1
285 return _parse_plain, parts, offset + 1
261 parts[-1] += s[offset : offset + 1]
286 parts[-1] += s[offset : offset + 1]
262 return _parse_plain, parts, offset + 1
287 return _parse_plain, parts, offset + 1
263
288
264 def _parse_quote(parts, s, offset):
289 def _parse_quote(parts, s, offset):
265 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
290 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
266 parts.append(b'')
291 parts.append(b'')
267 offset += 1
292 offset += 1
268 while offset < len(s) and (
293 while offset < len(s) and (
269 s[offset : offset + 1].isspace()
294 s[offset : offset + 1].isspace()
270 or s[offset : offset + 1] == b','
295 or s[offset : offset + 1] == b','
271 ):
296 ):
272 offset += 1
297 offset += 1
273 return _parse_plain, parts, offset
298 return _parse_plain, parts, offset
274
299
275 while offset < len(s) and s[offset : offset + 1] != b'"':
300 while offset < len(s) and s[offset : offset + 1] != b'"':
276 if (
301 if (
277 s[offset : offset + 1] == b'\\'
302 s[offset : offset + 1] == b'\\'
278 and offset + 1 < len(s)
303 and offset + 1 < len(s)
279 and s[offset + 1 : offset + 2] == b'"'
304 and s[offset + 1 : offset + 2] == b'"'
280 ):
305 ):
281 offset += 1
306 offset += 1
282 parts[-1] += b'"'
307 parts[-1] += b'"'
283 else:
308 else:
284 parts[-1] += s[offset : offset + 1]
309 parts[-1] += s[offset : offset + 1]
285 offset += 1
310 offset += 1
286
311
287 if offset >= len(s):
312 if offset >= len(s):
288 real_parts = _configlist(parts[-1])
313 real_parts = _configlist(parts[-1])
289 if not real_parts:
314 if not real_parts:
290 parts[-1] = b'"'
315 parts[-1] = b'"'
291 else:
316 else:
292 real_parts[0] = b'"' + real_parts[0]
317 real_parts[0] = b'"' + real_parts[0]
293 parts = parts[:-1]
318 parts = parts[:-1]
294 parts.extend(real_parts)
319 parts.extend(real_parts)
295 return None, parts, offset
320 return None, parts, offset
296
321
297 offset += 1
322 offset += 1
298 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
323 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
299 offset += 1
324 offset += 1
300
325
301 if offset < len(s):
326 if offset < len(s):
302 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
327 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
303 parts[-1] += b'"'
328 parts[-1] += b'"'
304 offset += 1
329 offset += 1
305 else:
330 else:
306 parts.append(b'')
331 parts.append(b'')
307 else:
332 else:
308 return None, parts, offset
333 return None, parts, offset
309
334
310 return _parse_plain, parts, offset
335 return _parse_plain, parts, offset
311
336
312 def _configlist(s):
337 def _configlist(s):
313 s = s.rstrip(b' ,')
338 s = s.rstrip(b' ,')
314 if not s:
339 if not s:
315 return []
340 return []
316 parser, parts, offset = _parse_plain, [b''], 0
341 parser, parts, offset = _parse_plain, [b''], 0
317 while parser:
342 while parser:
318 parser, parts, offset = parser(parts, s, offset)
343 parser, parts, offset = parser(parts, s, offset)
319 return parts
344 return parts
320
345
321 if value is not None and isinstance(value, bytes):
346 if value is not None and isinstance(value, bytes):
322 result = _configlist(value.lstrip(b' ,\n'))
347 result = _configlist(value.lstrip(b' ,\n'))
323 else:
348 else:
324 result = value
349 result = value
325 return result or []
350 return result or []
@@ -1,2391 +1,2398 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 datetime
12 import datetime
13 import errno
13 import errno
14 import getpass
14 import getpass
15 import inspect
15 import inspect
16 import os
16 import os
17 import re
17 import re
18 import signal
18 import signal
19 import socket
19 import socket
20 import subprocess
20 import subprocess
21 import sys
21 import sys
22 import traceback
22 import traceback
23
23
24 from .i18n import _
24 from .i18n import _
25 from .node import hex
25 from .node import hex
26 from .pycompat import (
26 from .pycompat import (
27 getattr,
27 getattr,
28 open,
28 open,
29 setattr,
29 setattr,
30 )
30 )
31
31
32 from . import (
32 from . import (
33 color,
33 color,
34 config,
34 config,
35 configitems,
35 configitems,
36 encoding,
36 encoding,
37 error,
37 error,
38 formatter,
38 formatter,
39 loggingutil,
39 loggingutil,
40 progress,
40 progress,
41 pycompat,
41 pycompat,
42 rcutil,
42 rcutil,
43 scmutil,
43 scmutil,
44 util,
44 util,
45 )
45 )
46 from .utils import (
46 from .utils import (
47 dateutil,
47 dateutil,
48 procutil,
48 procutil,
49 resourceutil,
49 resourceutil,
50 stringutil,
50 stringutil,
51 )
51 )
52
52
53 urlreq = util.urlreq
53 urlreq = util.urlreq
54
54
55 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
55 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
56 _keepalnum = b''.join(
56 _keepalnum = b''.join(
57 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
57 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
58 )
58 )
59
59
60 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
60 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
61 tweakrc = b"""
61 tweakrc = b"""
62 [ui]
62 [ui]
63 # The rollback command is dangerous. As a rule, don't use it.
63 # The rollback command is dangerous. As a rule, don't use it.
64 rollback = False
64 rollback = False
65 # Make `hg status` report copy information
65 # Make `hg status` report copy information
66 statuscopies = yes
66 statuscopies = yes
67 # Prefer curses UIs when available. Revert to plain-text with `text`.
67 # Prefer curses UIs when available. Revert to plain-text with `text`.
68 interface = curses
68 interface = curses
69 # Make compatible commands emit cwd-relative paths by default.
69 # Make compatible commands emit cwd-relative paths by default.
70 relative-paths = yes
70 relative-paths = yes
71
71
72 [commands]
72 [commands]
73 # Grep working directory by default.
73 # Grep working directory by default.
74 grep.all-files = True
74 grep.all-files = True
75 # Refuse to perform an `hg update` that would cause a file content merge
75 # Refuse to perform an `hg update` that would cause a file content merge
76 update.check = noconflict
76 update.check = noconflict
77 # Show conflicts information in `hg status`
77 # Show conflicts information in `hg status`
78 status.verbose = True
78 status.verbose = True
79 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
79 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
80 resolve.explicit-re-merge = True
80 resolve.explicit-re-merge = True
81
81
82 [diff]
82 [diff]
83 git = 1
83 git = 1
84 showfunc = 1
84 showfunc = 1
85 word-diff = 1
85 word-diff = 1
86 """
86 """
87
87
88 samplehgrcs = {
88 samplehgrcs = {
89 b'user': b"""# example user config (see 'hg help config' for more info)
89 b'user': b"""# example user config (see 'hg help config' for more info)
90 [ui]
90 [ui]
91 # name and email, e.g.
91 # name and email, e.g.
92 # username = Jane Doe <jdoe@example.com>
92 # username = Jane Doe <jdoe@example.com>
93 username =
93 username =
94
94
95 # We recommend enabling tweakdefaults to get slight improvements to
95 # We recommend enabling tweakdefaults to get slight improvements to
96 # the UI over time. Make sure to set HGPLAIN in the environment when
96 # the UI over time. Make sure to set HGPLAIN in the environment when
97 # writing scripts!
97 # writing scripts!
98 # tweakdefaults = True
98 # tweakdefaults = True
99
99
100 # uncomment to disable color in command output
100 # uncomment to disable color in command output
101 # (see 'hg help color' for details)
101 # (see 'hg help color' for details)
102 # color = never
102 # color = never
103
103
104 # uncomment to disable command output pagination
104 # uncomment to disable command output pagination
105 # (see 'hg help pager' for details)
105 # (see 'hg help pager' for details)
106 # paginate = never
106 # paginate = never
107
107
108 [extensions]
108 [extensions]
109 # uncomment the lines below to enable some popular extensions
109 # uncomment the lines below to enable some popular extensions
110 # (see 'hg help extensions' for more info)
110 # (see 'hg help extensions' for more info)
111 #
111 #
112 # histedit =
112 # histedit =
113 # rebase =
113 # rebase =
114 # uncommit =
114 # uncommit =
115 """,
115 """,
116 b'cloned': b"""# example repository config (see 'hg help config' for more info)
116 b'cloned': b"""# example repository config (see 'hg help config' for more info)
117 [paths]
117 [paths]
118 default = %s
118 default = %s
119
119
120 # path aliases to other clones of this repo in URLs or filesystem paths
120 # path aliases to other clones of this repo in URLs or filesystem paths
121 # (see 'hg help config.paths' for more info)
121 # (see 'hg help config.paths' for more info)
122 #
122 #
123 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
123 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
124 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
124 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
125 # my-clone = /home/jdoe/jdoes-clone
125 # my-clone = /home/jdoe/jdoes-clone
126
126
127 [ui]
127 [ui]
128 # name and email (local to this repository, optional), e.g.
128 # name and email (local to this repository, optional), e.g.
129 # username = Jane Doe <jdoe@example.com>
129 # username = Jane Doe <jdoe@example.com>
130 """,
130 """,
131 b'local': b"""# example repository config (see 'hg help config' for more info)
131 b'local': b"""# example repository config (see 'hg help config' for more info)
132 [paths]
132 [paths]
133 # path aliases to other clones of this repo in URLs or filesystem paths
133 # path aliases to other clones of this repo in URLs or filesystem paths
134 # (see 'hg help config.paths' for more info)
134 # (see 'hg help config.paths' for more info)
135 #
135 #
136 # default = http://example.com/hg/example-repo
136 # default = http://example.com/hg/example-repo
137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
137 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
138 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
139 # my-clone = /home/jdoe/jdoes-clone
139 # my-clone = /home/jdoe/jdoes-clone
140
140
141 [ui]
141 [ui]
142 # name and email (local to this repository, optional), e.g.
142 # name and email (local to this repository, optional), e.g.
143 # username = Jane Doe <jdoe@example.com>
143 # username = Jane Doe <jdoe@example.com>
144 """,
144 """,
145 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
145 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
146
146
147 [ui]
147 [ui]
148 # uncomment to disable color in command output
148 # uncomment to disable color in command output
149 # (see 'hg help color' for details)
149 # (see 'hg help color' for details)
150 # color = never
150 # color = never
151
151
152 # uncomment to disable command output pagination
152 # uncomment to disable command output pagination
153 # (see 'hg help pager' for details)
153 # (see 'hg help pager' for details)
154 # paginate = never
154 # paginate = never
155
155
156 [extensions]
156 [extensions]
157 # uncomment the lines below to enable some popular extensions
157 # uncomment the lines below to enable some popular extensions
158 # (see 'hg help extensions' for more info)
158 # (see 'hg help extensions' for more info)
159 #
159 #
160 # blackbox =
160 # blackbox =
161 # churn =
161 # churn =
162 """,
162 """,
163 }
163 }
164
164
165
165
166 def _maybestrurl(maybebytes):
166 def _maybestrurl(maybebytes):
167 return pycompat.rapply(pycompat.strurl, maybebytes)
167 return pycompat.rapply(pycompat.strurl, maybebytes)
168
168
169
169
170 def _maybebytesurl(maybestr):
170 def _maybebytesurl(maybestr):
171 return pycompat.rapply(pycompat.bytesurl, maybestr)
171 return pycompat.rapply(pycompat.bytesurl, maybestr)
172
172
173
173
174 class httppasswordmgrdbproxy(object):
174 class httppasswordmgrdbproxy(object):
175 """Delays loading urllib2 until it's needed."""
175 """Delays loading urllib2 until it's needed."""
176
176
177 def __init__(self):
177 def __init__(self):
178 self._mgr = None
178 self._mgr = None
179
179
180 def _get_mgr(self):
180 def _get_mgr(self):
181 if self._mgr is None:
181 if self._mgr is None:
182 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
182 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
183 return self._mgr
183 return self._mgr
184
184
185 def add_password(self, realm, uris, user, passwd):
185 def add_password(self, realm, uris, user, passwd):
186 return self._get_mgr().add_password(
186 return self._get_mgr().add_password(
187 _maybestrurl(realm),
187 _maybestrurl(realm),
188 _maybestrurl(uris),
188 _maybestrurl(uris),
189 _maybestrurl(user),
189 _maybestrurl(user),
190 _maybestrurl(passwd),
190 _maybestrurl(passwd),
191 )
191 )
192
192
193 def find_user_password(self, realm, uri):
193 def find_user_password(self, realm, uri):
194 mgr = self._get_mgr()
194 mgr = self._get_mgr()
195 return _maybebytesurl(
195 return _maybebytesurl(
196 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
196 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
197 )
197 )
198
198
199
199
200 def _catchterm(*args):
200 def _catchterm(*args):
201 raise error.SignalInterrupt
201 raise error.SignalInterrupt
202
202
203
203
204 # unique object used to detect no default value has been provided when
204 # unique object used to detect no default value has been provided when
205 # retrieving configuration value.
205 # retrieving configuration value.
206 _unset = object()
206 _unset = object()
207
207
208 # _reqexithandlers: callbacks run at the end of a request
208 # _reqexithandlers: callbacks run at the end of a request
209 _reqexithandlers = []
209 _reqexithandlers = []
210
210
211
211
212 class ui(object):
212 class ui(object):
213 def __init__(self, src=None):
213 def __init__(self, src=None):
214 """Create a fresh new ui object if no src given
214 """Create a fresh new ui object if no src given
215
215
216 Use uimod.ui.load() to create a ui which knows global and user configs.
216 Use uimod.ui.load() to create a ui which knows global and user configs.
217 In most cases, you should use ui.copy() to create a copy of an existing
217 In most cases, you should use ui.copy() to create a copy of an existing
218 ui object.
218 ui object.
219 """
219 """
220 # _buffers: used for temporary capture of output
220 # _buffers: used for temporary capture of output
221 self._buffers = []
221 self._buffers = []
222 # 3-tuple describing how each buffer in the stack behaves.
222 # 3-tuple describing how each buffer in the stack behaves.
223 # Values are (capture stderr, capture subprocesses, apply labels).
223 # Values are (capture stderr, capture subprocesses, apply labels).
224 self._bufferstates = []
224 self._bufferstates = []
225 # When a buffer is active, defines whether we are expanding labels.
225 # When a buffer is active, defines whether we are expanding labels.
226 # This exists to prevent an extra list lookup.
226 # This exists to prevent an extra list lookup.
227 self._bufferapplylabels = None
227 self._bufferapplylabels = None
228 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
228 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
229 self._reportuntrusted = True
229 self._reportuntrusted = True
230 self._knownconfig = configitems.coreitems
230 self._knownconfig = configitems.coreitems
231 self._ocfg = config.config() # overlay
231 self._ocfg = config.config() # overlay
232 self._tcfg = config.config() # trusted
232 self._tcfg = config.config() # trusted
233 self._ucfg = config.config() # untrusted
233 self._ucfg = config.config() # untrusted
234 self._trustusers = set()
234 self._trustusers = set()
235 self._trustgroups = set()
235 self._trustgroups = set()
236 self.callhooks = True
236 self.callhooks = True
237 # Insecure server connections requested.
237 # Insecure server connections requested.
238 self.insecureconnections = False
238 self.insecureconnections = False
239 # Blocked time
239 # Blocked time
240 self.logblockedtimes = False
240 self.logblockedtimes = False
241 # color mode: see mercurial/color.py for possible value
241 # color mode: see mercurial/color.py for possible value
242 self._colormode = None
242 self._colormode = None
243 self._terminfoparams = {}
243 self._terminfoparams = {}
244 self._styles = {}
244 self._styles = {}
245 self._uninterruptible = False
245 self._uninterruptible = False
246 self.showtimestamp = False
246 self.showtimestamp = False
247
247
248 if src:
248 if src:
249 self._fout = src._fout
249 self._fout = src._fout
250 self._ferr = src._ferr
250 self._ferr = src._ferr
251 self._fin = src._fin
251 self._fin = src._fin
252 self._fmsg = src._fmsg
252 self._fmsg = src._fmsg
253 self._fmsgout = src._fmsgout
253 self._fmsgout = src._fmsgout
254 self._fmsgerr = src._fmsgerr
254 self._fmsgerr = src._fmsgerr
255 self._finoutredirected = src._finoutredirected
255 self._finoutredirected = src._finoutredirected
256 self._loggers = src._loggers.copy()
256 self._loggers = src._loggers.copy()
257 self.pageractive = src.pageractive
257 self.pageractive = src.pageractive
258 self._disablepager = src._disablepager
258 self._disablepager = src._disablepager
259 self._tweaked = src._tweaked
259 self._tweaked = src._tweaked
260
260
261 self._tcfg = src._tcfg.copy()
261 self._tcfg = src._tcfg.copy()
262 self._ucfg = src._ucfg.copy()
262 self._ucfg = src._ucfg.copy()
263 self._ocfg = src._ocfg.copy()
263 self._ocfg = src._ocfg.copy()
264 self._trustusers = src._trustusers.copy()
264 self._trustusers = src._trustusers.copy()
265 self._trustgroups = src._trustgroups.copy()
265 self._trustgroups = src._trustgroups.copy()
266 self.environ = src.environ
266 self.environ = src.environ
267 self.callhooks = src.callhooks
267 self.callhooks = src.callhooks
268 self.insecureconnections = src.insecureconnections
268 self.insecureconnections = src.insecureconnections
269 self._colormode = src._colormode
269 self._colormode = src._colormode
270 self._terminfoparams = src._terminfoparams.copy()
270 self._terminfoparams = src._terminfoparams.copy()
271 self._styles = src._styles.copy()
271 self._styles = src._styles.copy()
272
272
273 self.fixconfig()
273 self.fixconfig()
274
274
275 self.httppasswordmgrdb = src.httppasswordmgrdb
275 self.httppasswordmgrdb = src.httppasswordmgrdb
276 self._blockedtimes = src._blockedtimes
276 self._blockedtimes = src._blockedtimes
277 else:
277 else:
278 self._fout = procutil.stdout
278 self._fout = procutil.stdout
279 self._ferr = procutil.stderr
279 self._ferr = procutil.stderr
280 self._fin = procutil.stdin
280 self._fin = procutil.stdin
281 self._fmsg = None
281 self._fmsg = None
282 self._fmsgout = self.fout # configurable
282 self._fmsgout = self.fout # configurable
283 self._fmsgerr = self.ferr # configurable
283 self._fmsgerr = self.ferr # configurable
284 self._finoutredirected = False
284 self._finoutredirected = False
285 self._loggers = {}
285 self._loggers = {}
286 self.pageractive = False
286 self.pageractive = False
287 self._disablepager = False
287 self._disablepager = False
288 self._tweaked = False
288 self._tweaked = False
289
289
290 # shared read-only environment
290 # shared read-only environment
291 self.environ = encoding.environ
291 self.environ = encoding.environ
292
292
293 self.httppasswordmgrdb = httppasswordmgrdbproxy()
293 self.httppasswordmgrdb = httppasswordmgrdbproxy()
294 self._blockedtimes = collections.defaultdict(int)
294 self._blockedtimes = collections.defaultdict(int)
295
295
296 allowed = self.configlist(b'experimental', b'exportableenviron')
296 allowed = self.configlist(b'experimental', b'exportableenviron')
297 if b'*' in allowed:
297 if b'*' in allowed:
298 self._exportableenviron = self.environ
298 self._exportableenviron = self.environ
299 else:
299 else:
300 self._exportableenviron = {}
300 self._exportableenviron = {}
301 for k in allowed:
301 for k in allowed:
302 if k in self.environ:
302 if k in self.environ:
303 self._exportableenviron[k] = self.environ[k]
303 self._exportableenviron[k] = self.environ[k]
304
304
305 def _new_source(self):
306 self._ocfg.new_source()
307 self._tcfg.new_source()
308 self._ucfg.new_source()
309
305 @classmethod
310 @classmethod
306 def load(cls):
311 def load(cls):
307 """Create a ui and load global and user configs"""
312 """Create a ui and load global and user configs"""
308 u = cls()
313 u = cls()
309 # we always trust global config files and environment variables
314 # we always trust global config files and environment variables
310 for t, f in rcutil.rccomponents():
315 for t, f in rcutil.rccomponents():
311 if t == b'path':
316 if t == b'path':
312 u.readconfig(f, trust=True)
317 u.readconfig(f, trust=True)
313 elif t == b'resource':
318 elif t == b'resource':
314 u.read_resource_config(f, trust=True)
319 u.read_resource_config(f, trust=True)
315 elif t == b'items':
320 elif t == b'items':
321 u._new_source()
316 sections = set()
322 sections = set()
317 for section, name, value, source in f:
323 for section, name, value, source in f:
318 # do not set u._ocfg
324 # do not set u._ocfg
319 # XXX clean this up once immutable config object is a thing
325 # XXX clean this up once immutable config object is a thing
320 u._tcfg.set(section, name, value, source)
326 u._tcfg.set(section, name, value, source)
321 u._ucfg.set(section, name, value, source)
327 u._ucfg.set(section, name, value, source)
322 sections.add(section)
328 sections.add(section)
323 for section in sections:
329 for section in sections:
324 u.fixconfig(section=section)
330 u.fixconfig(section=section)
325 else:
331 else:
326 raise error.ProgrammingError(b'unknown rctype: %s' % t)
332 raise error.ProgrammingError(b'unknown rctype: %s' % t)
327 u._maybetweakdefaults()
333 u._maybetweakdefaults()
334 u._new_source() # anything after that is a different level
328 return u
335 return u
329
336
330 def _maybetweakdefaults(self):
337 def _maybetweakdefaults(self):
331 if not self.configbool(b'ui', b'tweakdefaults'):
338 if not self.configbool(b'ui', b'tweakdefaults'):
332 return
339 return
333 if self._tweaked or self.plain(b'tweakdefaults'):
340 if self._tweaked or self.plain(b'tweakdefaults'):
334 return
341 return
335
342
336 # Note: it is SUPER IMPORTANT that you set self._tweaked to
343 # Note: it is SUPER IMPORTANT that you set self._tweaked to
337 # True *before* any calls to setconfig(), otherwise you'll get
344 # True *before* any calls to setconfig(), otherwise you'll get
338 # infinite recursion between setconfig and this method.
345 # infinite recursion between setconfig and this method.
339 #
346 #
340 # TODO: We should extract an inner method in setconfig() to
347 # TODO: We should extract an inner method in setconfig() to
341 # avoid this weirdness.
348 # avoid this weirdness.
342 self._tweaked = True
349 self._tweaked = True
343 tmpcfg = config.config()
350 tmpcfg = config.config()
344 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
351 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
345 for section in tmpcfg:
352 for section in tmpcfg:
346 for name, value in tmpcfg.items(section):
353 for name, value in tmpcfg.items(section):
347 if not self.hasconfig(section, name):
354 if not self.hasconfig(section, name):
348 self.setconfig(section, name, value, b"<tweakdefaults>")
355 self.setconfig(section, name, value, b"<tweakdefaults>")
349
356
350 def copy(self):
357 def copy(self):
351 return self.__class__(self)
358 return self.__class__(self)
352
359
353 def resetstate(self):
360 def resetstate(self):
354 """Clear internal state that shouldn't persist across commands"""
361 """Clear internal state that shouldn't persist across commands"""
355 if self._progbar:
362 if self._progbar:
356 self._progbar.resetstate() # reset last-print time of progress bar
363 self._progbar.resetstate() # reset last-print time of progress bar
357 self.httppasswordmgrdb = httppasswordmgrdbproxy()
364 self.httppasswordmgrdb = httppasswordmgrdbproxy()
358
365
359 @contextlib.contextmanager
366 @contextlib.contextmanager
360 def timeblockedsection(self, key):
367 def timeblockedsection(self, key):
361 # this is open-coded below - search for timeblockedsection to find them
368 # this is open-coded below - search for timeblockedsection to find them
362 starttime = util.timer()
369 starttime = util.timer()
363 try:
370 try:
364 yield
371 yield
365 finally:
372 finally:
366 self._blockedtimes[key + b'_blocked'] += (
373 self._blockedtimes[key + b'_blocked'] += (
367 util.timer() - starttime
374 util.timer() - starttime
368 ) * 1000
375 ) * 1000
369
376
370 @contextlib.contextmanager
377 @contextlib.contextmanager
371 def uninterruptible(self):
378 def uninterruptible(self):
372 """Mark an operation as unsafe.
379 """Mark an operation as unsafe.
373
380
374 Most operations on a repository are safe to interrupt, but a
381 Most operations on a repository are safe to interrupt, but a
375 few are risky (for example repair.strip). This context manager
382 few are risky (for example repair.strip). This context manager
376 lets you advise Mercurial that something risky is happening so
383 lets you advise Mercurial that something risky is happening so
377 that control-C etc can be blocked if desired.
384 that control-C etc can be blocked if desired.
378 """
385 """
379 enabled = self.configbool(b'experimental', b'nointerrupt')
386 enabled = self.configbool(b'experimental', b'nointerrupt')
380 if enabled and self.configbool(
387 if enabled and self.configbool(
381 b'experimental', b'nointerrupt-interactiveonly'
388 b'experimental', b'nointerrupt-interactiveonly'
382 ):
389 ):
383 enabled = self.interactive()
390 enabled = self.interactive()
384 if self._uninterruptible or not enabled:
391 if self._uninterruptible or not enabled:
385 # if nointerrupt support is turned off, the process isn't
392 # if nointerrupt support is turned off, the process isn't
386 # interactive, or we're already in an uninterruptible
393 # interactive, or we're already in an uninterruptible
387 # block, do nothing.
394 # block, do nothing.
388 yield
395 yield
389 return
396 return
390
397
391 def warn():
398 def warn():
392 self.warn(_(b"shutting down cleanly\n"))
399 self.warn(_(b"shutting down cleanly\n"))
393 self.warn(
400 self.warn(
394 _(b"press ^C again to terminate immediately (dangerous)\n")
401 _(b"press ^C again to terminate immediately (dangerous)\n")
395 )
402 )
396 return True
403 return True
397
404
398 with procutil.uninterruptible(warn):
405 with procutil.uninterruptible(warn):
399 try:
406 try:
400 self._uninterruptible = True
407 self._uninterruptible = True
401 yield
408 yield
402 finally:
409 finally:
403 self._uninterruptible = False
410 self._uninterruptible = False
404
411
405 def formatter(self, topic, opts):
412 def formatter(self, topic, opts):
406 return formatter.formatter(self, self, topic, opts)
413 return formatter.formatter(self, self, topic, opts)
407
414
408 def _trusted(self, fp, f):
415 def _trusted(self, fp, f):
409 st = util.fstat(fp)
416 st = util.fstat(fp)
410 if util.isowner(st):
417 if util.isowner(st):
411 return True
418 return True
412
419
413 tusers, tgroups = self._trustusers, self._trustgroups
420 tusers, tgroups = self._trustusers, self._trustgroups
414 if b'*' in tusers or b'*' in tgroups:
421 if b'*' in tusers or b'*' in tgroups:
415 return True
422 return True
416
423
417 user = util.username(st.st_uid)
424 user = util.username(st.st_uid)
418 group = util.groupname(st.st_gid)
425 group = util.groupname(st.st_gid)
419 if user in tusers or group in tgroups or user == util.username():
426 if user in tusers or group in tgroups or user == util.username():
420 return True
427 return True
421
428
422 if self._reportuntrusted:
429 if self._reportuntrusted:
423 self.warn(
430 self.warn(
424 _(
431 _(
425 b'not trusting file %s from untrusted '
432 b'not trusting file %s from untrusted '
426 b'user %s, group %s\n'
433 b'user %s, group %s\n'
427 )
434 )
428 % (f, user, group)
435 % (f, user, group)
429 )
436 )
430 return False
437 return False
431
438
432 def read_resource_config(
439 def read_resource_config(
433 self, name, root=None, trust=False, sections=None, remap=None
440 self, name, root=None, trust=False, sections=None, remap=None
434 ):
441 ):
435 try:
442 try:
436 fp = resourceutil.open_resource(name[0], name[1])
443 fp = resourceutil.open_resource(name[0], name[1])
437 except IOError:
444 except IOError:
438 if not sections: # ignore unless we were looking for something
445 if not sections: # ignore unless we were looking for something
439 return
446 return
440 raise
447 raise
441
448
442 self._readconfig(
449 self._readconfig(
443 b'resource:%s.%s' % name, fp, root, trust, sections, remap
450 b'resource:%s.%s' % name, fp, root, trust, sections, remap
444 )
451 )
445
452
446 def readconfig(
453 def readconfig(
447 self, filename, root=None, trust=False, sections=None, remap=None
454 self, filename, root=None, trust=False, sections=None, remap=None
448 ):
455 ):
449 try:
456 try:
450 fp = open(filename, 'rb')
457 fp = open(filename, 'rb')
451 except IOError:
458 except IOError:
452 if not sections: # ignore unless we were looking for something
459 if not sections: # ignore unless we were looking for something
453 return
460 return
454 raise
461 raise
455
462
456 self._readconfig(filename, fp, root, trust, sections, remap)
463 self._readconfig(filename, fp, root, trust, sections, remap)
457
464
458 def _readconfig(
465 def _readconfig(
459 self, filename, fp, root=None, trust=False, sections=None, remap=None
466 self, filename, fp, root=None, trust=False, sections=None, remap=None
460 ):
467 ):
461 with fp:
468 with fp:
462 cfg = config.config()
469 cfg = config.config()
463 trusted = sections or trust or self._trusted(fp, filename)
470 trusted = sections or trust or self._trusted(fp, filename)
464
471
465 try:
472 try:
466 cfg.read(filename, fp, sections=sections, remap=remap)
473 cfg.read(filename, fp, sections=sections, remap=remap)
467 except error.ConfigError as inst:
474 except error.ConfigError as inst:
468 if trusted:
475 if trusted:
469 raise
476 raise
470 self.warn(
477 self.warn(
471 _(b'ignored %s: %s\n') % (inst.location, inst.message)
478 _(b'ignored %s: %s\n') % (inst.location, inst.message)
472 )
479 )
473
480
474 self._applyconfig(cfg, trusted, root)
481 self._applyconfig(cfg, trusted, root)
475
482
476 def applyconfig(self, configitems, source=b"", root=None):
483 def applyconfig(self, configitems, source=b"", root=None):
477 """Add configitems from a non-file source. Unlike with ``setconfig()``,
484 """Add configitems from a non-file source. Unlike with ``setconfig()``,
478 they can be overridden by subsequent config file reads. The items are
485 they can be overridden by subsequent config file reads. The items are
479 in the same format as ``configoverride()``, namely a dict of the
486 in the same format as ``configoverride()``, namely a dict of the
480 following structures: {(section, name) : value}
487 following structures: {(section, name) : value}
481
488
482 Typically this is used by extensions that inject themselves into the
489 Typically this is used by extensions that inject themselves into the
483 config file load procedure by monkeypatching ``localrepo.loadhgrc()``.
490 config file load procedure by monkeypatching ``localrepo.loadhgrc()``.
484 """
491 """
485 cfg = config.config()
492 cfg = config.config()
486
493
487 for (section, name), value in configitems.items():
494 for (section, name), value in configitems.items():
488 cfg.set(section, name, value, source)
495 cfg.set(section, name, value, source)
489
496
490 self._applyconfig(cfg, True, root)
497 self._applyconfig(cfg, True, root)
491
498
492 def _applyconfig(self, cfg, trusted, root):
499 def _applyconfig(self, cfg, trusted, root):
493 if self.plain():
500 if self.plain():
494 for k in (
501 for k in (
495 b'debug',
502 b'debug',
496 b'fallbackencoding',
503 b'fallbackencoding',
497 b'quiet',
504 b'quiet',
498 b'slash',
505 b'slash',
499 b'logtemplate',
506 b'logtemplate',
500 b'message-output',
507 b'message-output',
501 b'statuscopies',
508 b'statuscopies',
502 b'style',
509 b'style',
503 b'traceback',
510 b'traceback',
504 b'verbose',
511 b'verbose',
505 ):
512 ):
506 if k in cfg[b'ui']:
513 if k in cfg[b'ui']:
507 del cfg[b'ui'][k]
514 del cfg[b'ui'][k]
508 for k, v in cfg.items(b'defaults'):
515 for k, v in cfg.items(b'defaults'):
509 del cfg[b'defaults'][k]
516 del cfg[b'defaults'][k]
510 for k, v in cfg.items(b'commands'):
517 for k, v in cfg.items(b'commands'):
511 del cfg[b'commands'][k]
518 del cfg[b'commands'][k]
512 for k, v in cfg.items(b'command-templates'):
519 for k, v in cfg.items(b'command-templates'):
513 del cfg[b'command-templates'][k]
520 del cfg[b'command-templates'][k]
514 # Don't remove aliases from the configuration if in the exceptionlist
521 # Don't remove aliases from the configuration if in the exceptionlist
515 if self.plain(b'alias'):
522 if self.plain(b'alias'):
516 for k, v in cfg.items(b'alias'):
523 for k, v in cfg.items(b'alias'):
517 del cfg[b'alias'][k]
524 del cfg[b'alias'][k]
518 if self.plain(b'revsetalias'):
525 if self.plain(b'revsetalias'):
519 for k, v in cfg.items(b'revsetalias'):
526 for k, v in cfg.items(b'revsetalias'):
520 del cfg[b'revsetalias'][k]
527 del cfg[b'revsetalias'][k]
521 if self.plain(b'templatealias'):
528 if self.plain(b'templatealias'):
522 for k, v in cfg.items(b'templatealias'):
529 for k, v in cfg.items(b'templatealias'):
523 del cfg[b'templatealias'][k]
530 del cfg[b'templatealias'][k]
524
531
525 if trusted:
532 if trusted:
526 self._tcfg.update(cfg)
533 self._tcfg.update(cfg)
527 self._tcfg.update(self._ocfg)
534 self._tcfg.update(self._ocfg)
528 self._ucfg.update(cfg)
535 self._ucfg.update(cfg)
529 self._ucfg.update(self._ocfg)
536 self._ucfg.update(self._ocfg)
530
537
531 if root is None:
538 if root is None:
532 root = os.path.expanduser(b'~')
539 root = os.path.expanduser(b'~')
533 self.fixconfig(root=root)
540 self.fixconfig(root=root)
534
541
535 def fixconfig(self, root=None, section=None):
542 def fixconfig(self, root=None, section=None):
536 if section in (None, b'paths'):
543 if section in (None, b'paths'):
537 # expand vars and ~
544 # expand vars and ~
538 # translate paths relative to root (or home) into absolute paths
545 # translate paths relative to root (or home) into absolute paths
539 root = root or encoding.getcwd()
546 root = root or encoding.getcwd()
540 for c in self._tcfg, self._ucfg, self._ocfg:
547 for c in self._tcfg, self._ucfg, self._ocfg:
541 for n, p in c.items(b'paths'):
548 for n, p in c.items(b'paths'):
542 # Ignore sub-options.
549 # Ignore sub-options.
543 if b':' in n:
550 if b':' in n:
544 continue
551 continue
545 if not p:
552 if not p:
546 continue
553 continue
547 if b'%%' in p:
554 if b'%%' in p:
548 s = self.configsource(b'paths', n) or b'none'
555 s = self.configsource(b'paths', n) or b'none'
549 self.warn(
556 self.warn(
550 _(b"(deprecated '%%' in path %s=%s from %s)\n")
557 _(b"(deprecated '%%' in path %s=%s from %s)\n")
551 % (n, p, s)
558 % (n, p, s)
552 )
559 )
553 p = p.replace(b'%%', b'%')
560 p = p.replace(b'%%', b'%')
554 p = util.expandpath(p)
561 p = util.expandpath(p)
555 if not util.hasscheme(p) and not os.path.isabs(p):
562 if not util.hasscheme(p) and not os.path.isabs(p):
556 p = os.path.normpath(os.path.join(root, p))
563 p = os.path.normpath(os.path.join(root, p))
557 c.alter(b"paths", n, p)
564 c.alter(b"paths", n, p)
558
565
559 if section in (None, b'ui'):
566 if section in (None, b'ui'):
560 # update ui options
567 # update ui options
561 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
568 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
562 self.debugflag = self.configbool(b'ui', b'debug')
569 self.debugflag = self.configbool(b'ui', b'debug')
563 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
570 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
564 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
571 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
565 if self.verbose and self.quiet:
572 if self.verbose and self.quiet:
566 self.quiet = self.verbose = False
573 self.quiet = self.verbose = False
567 self._reportuntrusted = self.debugflag or self.configbool(
574 self._reportuntrusted = self.debugflag or self.configbool(
568 b"ui", b"report_untrusted"
575 b"ui", b"report_untrusted"
569 )
576 )
570 self.showtimestamp = self.configbool(b'ui', b'timestamp-output')
577 self.showtimestamp = self.configbool(b'ui', b'timestamp-output')
571 self.tracebackflag = self.configbool(b'ui', b'traceback')
578 self.tracebackflag = self.configbool(b'ui', b'traceback')
572 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
579 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
573
580
574 if section in (None, b'trusted'):
581 if section in (None, b'trusted'):
575 # update trust information
582 # update trust information
576 self._trustusers.update(self.configlist(b'trusted', b'users'))
583 self._trustusers.update(self.configlist(b'trusted', b'users'))
577 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
584 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
578
585
579 if section in (None, b'devel', b'ui') and self.debugflag:
586 if section in (None, b'devel', b'ui') and self.debugflag:
580 tracked = set()
587 tracked = set()
581 if self.configbool(b'devel', b'debug.extensions'):
588 if self.configbool(b'devel', b'debug.extensions'):
582 tracked.add(b'extension')
589 tracked.add(b'extension')
583 if tracked:
590 if tracked:
584 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
591 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
585 self.setlogger(b'debug', logger)
592 self.setlogger(b'debug', logger)
586
593
587 def backupconfig(self, section, item):
594 def backupconfig(self, section, item):
588 return (
595 return (
589 self._ocfg.backup(section, item),
596 self._ocfg.backup(section, item),
590 self._tcfg.backup(section, item),
597 self._tcfg.backup(section, item),
591 self._ucfg.backup(section, item),
598 self._ucfg.backup(section, item),
592 )
599 )
593
600
594 def restoreconfig(self, data):
601 def restoreconfig(self, data):
595 self._ocfg.restore(data[0])
602 self._ocfg.restore(data[0])
596 self._tcfg.restore(data[1])
603 self._tcfg.restore(data[1])
597 self._ucfg.restore(data[2])
604 self._ucfg.restore(data[2])
598
605
599 def setconfig(self, section, name, value, source=b''):
606 def setconfig(self, section, name, value, source=b''):
600 for cfg in (self._ocfg, self._tcfg, self._ucfg):
607 for cfg in (self._ocfg, self._tcfg, self._ucfg):
601 cfg.set(section, name, value, source)
608 cfg.set(section, name, value, source)
602 self.fixconfig(section=section)
609 self.fixconfig(section=section)
603 self._maybetweakdefaults()
610 self._maybetweakdefaults()
604
611
605 def _data(self, untrusted):
612 def _data(self, untrusted):
606 return untrusted and self._ucfg or self._tcfg
613 return untrusted and self._ucfg or self._tcfg
607
614
608 def configsource(self, section, name, untrusted=False):
615 def configsource(self, section, name, untrusted=False):
609 return self._data(untrusted).source(section, name)
616 return self._data(untrusted).source(section, name)
610
617
611 def config(self, section, name, default=_unset, untrusted=False):
618 def config(self, section, name, default=_unset, untrusted=False):
612 """return the plain string version of a config"""
619 """return the plain string version of a config"""
613 value = self._config(
620 value = self._config(
614 section, name, default=default, untrusted=untrusted
621 section, name, default=default, untrusted=untrusted
615 )
622 )
616 if value is _unset:
623 if value is _unset:
617 return None
624 return None
618 return value
625 return value
619
626
620 def _config(self, section, name, default=_unset, untrusted=False):
627 def _config(self, section, name, default=_unset, untrusted=False):
621 value = itemdefault = default
628 value = itemdefault = default
622 item = self._knownconfig.get(section, {}).get(name)
629 item = self._knownconfig.get(section, {}).get(name)
623 alternates = [(section, name)]
630 alternates = [(section, name)]
624
631
625 if item is not None:
632 if item is not None:
626 alternates.extend(item.alias)
633 alternates.extend(item.alias)
627 if callable(item.default):
634 if callable(item.default):
628 itemdefault = item.default()
635 itemdefault = item.default()
629 else:
636 else:
630 itemdefault = item.default
637 itemdefault = item.default
631 else:
638 else:
632 msg = b"accessing unregistered config item: '%s.%s'"
639 msg = b"accessing unregistered config item: '%s.%s'"
633 msg %= (section, name)
640 msg %= (section, name)
634 self.develwarn(msg, 2, b'warn-config-unknown')
641 self.develwarn(msg, 2, b'warn-config-unknown')
635
642
636 if default is _unset:
643 if default is _unset:
637 if item is None:
644 if item is None:
638 value = default
645 value = default
639 elif item.default is configitems.dynamicdefault:
646 elif item.default is configitems.dynamicdefault:
640 value = None
647 value = None
641 msg = b"config item requires an explicit default value: '%s.%s'"
648 msg = b"config item requires an explicit default value: '%s.%s'"
642 msg %= (section, name)
649 msg %= (section, name)
643 self.develwarn(msg, 2, b'warn-config-default')
650 self.develwarn(msg, 2, b'warn-config-default')
644 else:
651 else:
645 value = itemdefault
652 value = itemdefault
646 elif (
653 elif (
647 item is not None
654 item is not None
648 and item.default is not configitems.dynamicdefault
655 and item.default is not configitems.dynamicdefault
649 and default != itemdefault
656 and default != itemdefault
650 ):
657 ):
651 msg = (
658 msg = (
652 b"specifying a mismatched default value for a registered "
659 b"specifying a mismatched default value for a registered "
653 b"config item: '%s.%s' '%s'"
660 b"config item: '%s.%s' '%s'"
654 )
661 )
655 msg %= (section, name, pycompat.bytestr(default))
662 msg %= (section, name, pycompat.bytestr(default))
656 self.develwarn(msg, 2, b'warn-config-default')
663 self.develwarn(msg, 2, b'warn-config-default')
657
664
658 for s, n in alternates:
665 for s, n in alternates:
659 candidate = self._data(untrusted).get(s, n, None)
666 candidate = self._data(untrusted).get(s, n, None)
660 if candidate is not None:
667 if candidate is not None:
661 value = candidate
668 value = candidate
662 break
669 break
663
670
664 if self.debugflag and not untrusted and self._reportuntrusted:
671 if self.debugflag and not untrusted and self._reportuntrusted:
665 for s, n in alternates:
672 for s, n in alternates:
666 uvalue = self._ucfg.get(s, n)
673 uvalue = self._ucfg.get(s, n)
667 if uvalue is not None and uvalue != value:
674 if uvalue is not None and uvalue != value:
668 self.debug(
675 self.debug(
669 b"ignoring untrusted configuration option "
676 b"ignoring untrusted configuration option "
670 b"%s.%s = %s\n" % (s, n, uvalue)
677 b"%s.%s = %s\n" % (s, n, uvalue)
671 )
678 )
672 return value
679 return value
673
680
674 def config_default(self, section, name):
681 def config_default(self, section, name):
675 """return the default value for a config option
682 """return the default value for a config option
676
683
677 The default is returned "raw", for example if it is a callable, the
684 The default is returned "raw", for example if it is a callable, the
678 callable was not called.
685 callable was not called.
679 """
686 """
680 item = self._knownconfig.get(section, {}).get(name)
687 item = self._knownconfig.get(section, {}).get(name)
681
688
682 if item is None:
689 if item is None:
683 raise KeyError((section, name))
690 raise KeyError((section, name))
684 return item.default
691 return item.default
685
692
686 def configsuboptions(self, section, name, default=_unset, untrusted=False):
693 def configsuboptions(self, section, name, default=_unset, untrusted=False):
687 """Get a config option and all sub-options.
694 """Get a config option and all sub-options.
688
695
689 Some config options have sub-options that are declared with the
696 Some config options have sub-options that are declared with the
690 format "key:opt = value". This method is used to return the main
697 format "key:opt = value". This method is used to return the main
691 option and all its declared sub-options.
698 option and all its declared sub-options.
692
699
693 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
700 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
694 is a dict of defined sub-options where keys and values are strings.
701 is a dict of defined sub-options where keys and values are strings.
695 """
702 """
696 main = self.config(section, name, default, untrusted=untrusted)
703 main = self.config(section, name, default, untrusted=untrusted)
697 data = self._data(untrusted)
704 data = self._data(untrusted)
698 sub = {}
705 sub = {}
699 prefix = b'%s:' % name
706 prefix = b'%s:' % name
700 for k, v in data.items(section):
707 for k, v in data.items(section):
701 if k.startswith(prefix):
708 if k.startswith(prefix):
702 sub[k[len(prefix) :]] = v
709 sub[k[len(prefix) :]] = v
703
710
704 if self.debugflag and not untrusted and self._reportuntrusted:
711 if self.debugflag and not untrusted and self._reportuntrusted:
705 for k, v in sub.items():
712 for k, v in sub.items():
706 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
713 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
707 if uvalue is not None and uvalue != v:
714 if uvalue is not None and uvalue != v:
708 self.debug(
715 self.debug(
709 b'ignoring untrusted configuration option '
716 b'ignoring untrusted configuration option '
710 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
717 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
711 )
718 )
712
719
713 return main, sub
720 return main, sub
714
721
715 def configpath(self, section, name, default=_unset, untrusted=False):
722 def configpath(self, section, name, default=_unset, untrusted=False):
716 """get a path config item, expanded relative to repo root or config
723 """get a path config item, expanded relative to repo root or config
717 file"""
724 file"""
718 v = self.config(section, name, default, untrusted)
725 v = self.config(section, name, default, untrusted)
719 if v is None:
726 if v is None:
720 return None
727 return None
721 if not os.path.isabs(v) or b"://" not in v:
728 if not os.path.isabs(v) or b"://" not in v:
722 src = self.configsource(section, name, untrusted)
729 src = self.configsource(section, name, untrusted)
723 if b':' in src:
730 if b':' in src:
724 base = os.path.dirname(src.rsplit(b':')[0])
731 base = os.path.dirname(src.rsplit(b':')[0])
725 v = os.path.join(base, os.path.expanduser(v))
732 v = os.path.join(base, os.path.expanduser(v))
726 return v
733 return v
727
734
728 def configbool(self, section, name, default=_unset, untrusted=False):
735 def configbool(self, section, name, default=_unset, untrusted=False):
729 """parse a configuration element as a boolean
736 """parse a configuration element as a boolean
730
737
731 >>> u = ui(); s = b'foo'
738 >>> u = ui(); s = b'foo'
732 >>> u.setconfig(s, b'true', b'yes')
739 >>> u.setconfig(s, b'true', b'yes')
733 >>> u.configbool(s, b'true')
740 >>> u.configbool(s, b'true')
734 True
741 True
735 >>> u.setconfig(s, b'false', b'no')
742 >>> u.setconfig(s, b'false', b'no')
736 >>> u.configbool(s, b'false')
743 >>> u.configbool(s, b'false')
737 False
744 False
738 >>> u.configbool(s, b'unknown')
745 >>> u.configbool(s, b'unknown')
739 False
746 False
740 >>> u.configbool(s, b'unknown', True)
747 >>> u.configbool(s, b'unknown', True)
741 True
748 True
742 >>> u.setconfig(s, b'invalid', b'somevalue')
749 >>> u.setconfig(s, b'invalid', b'somevalue')
743 >>> u.configbool(s, b'invalid')
750 >>> u.configbool(s, b'invalid')
744 Traceback (most recent call last):
751 Traceback (most recent call last):
745 ...
752 ...
746 ConfigError: foo.invalid is not a boolean ('somevalue')
753 ConfigError: foo.invalid is not a boolean ('somevalue')
747 """
754 """
748
755
749 v = self._config(section, name, default, untrusted=untrusted)
756 v = self._config(section, name, default, untrusted=untrusted)
750 if v is None:
757 if v is None:
751 return v
758 return v
752 if v is _unset:
759 if v is _unset:
753 if default is _unset:
760 if default is _unset:
754 return False
761 return False
755 return default
762 return default
756 if isinstance(v, bool):
763 if isinstance(v, bool):
757 return v
764 return v
758 b = stringutil.parsebool(v)
765 b = stringutil.parsebool(v)
759 if b is None:
766 if b is None:
760 raise error.ConfigError(
767 raise error.ConfigError(
761 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
768 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
762 )
769 )
763 return b
770 return b
764
771
765 def configwith(
772 def configwith(
766 self, convert, section, name, default=_unset, desc=None, untrusted=False
773 self, convert, section, name, default=_unset, desc=None, untrusted=False
767 ):
774 ):
768 """parse a configuration element with a conversion function
775 """parse a configuration element with a conversion function
769
776
770 >>> u = ui(); s = b'foo'
777 >>> u = ui(); s = b'foo'
771 >>> u.setconfig(s, b'float1', b'42')
778 >>> u.setconfig(s, b'float1', b'42')
772 >>> u.configwith(float, s, b'float1')
779 >>> u.configwith(float, s, b'float1')
773 42.0
780 42.0
774 >>> u.setconfig(s, b'float2', b'-4.25')
781 >>> u.setconfig(s, b'float2', b'-4.25')
775 >>> u.configwith(float, s, b'float2')
782 >>> u.configwith(float, s, b'float2')
776 -4.25
783 -4.25
777 >>> u.configwith(float, s, b'unknown', 7)
784 >>> u.configwith(float, s, b'unknown', 7)
778 7.0
785 7.0
779 >>> u.setconfig(s, b'invalid', b'somevalue')
786 >>> u.setconfig(s, b'invalid', b'somevalue')
780 >>> u.configwith(float, s, b'invalid')
787 >>> u.configwith(float, s, b'invalid')
781 Traceback (most recent call last):
788 Traceback (most recent call last):
782 ...
789 ...
783 ConfigError: foo.invalid is not a valid float ('somevalue')
790 ConfigError: foo.invalid is not a valid float ('somevalue')
784 >>> u.configwith(float, s, b'invalid', desc=b'womble')
791 >>> u.configwith(float, s, b'invalid', desc=b'womble')
785 Traceback (most recent call last):
792 Traceback (most recent call last):
786 ...
793 ...
787 ConfigError: foo.invalid is not a valid womble ('somevalue')
794 ConfigError: foo.invalid is not a valid womble ('somevalue')
788 """
795 """
789
796
790 v = self.config(section, name, default, untrusted)
797 v = self.config(section, name, default, untrusted)
791 if v is None:
798 if v is None:
792 return v # do not attempt to convert None
799 return v # do not attempt to convert None
793 try:
800 try:
794 return convert(v)
801 return convert(v)
795 except (ValueError, error.ParseError):
802 except (ValueError, error.ParseError):
796 if desc is None:
803 if desc is None:
797 desc = pycompat.sysbytes(convert.__name__)
804 desc = pycompat.sysbytes(convert.__name__)
798 raise error.ConfigError(
805 raise error.ConfigError(
799 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
806 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
800 )
807 )
801
808
802 def configint(self, section, name, default=_unset, untrusted=False):
809 def configint(self, section, name, default=_unset, untrusted=False):
803 """parse a configuration element as an integer
810 """parse a configuration element as an integer
804
811
805 >>> u = ui(); s = b'foo'
812 >>> u = ui(); s = b'foo'
806 >>> u.setconfig(s, b'int1', b'42')
813 >>> u.setconfig(s, b'int1', b'42')
807 >>> u.configint(s, b'int1')
814 >>> u.configint(s, b'int1')
808 42
815 42
809 >>> u.setconfig(s, b'int2', b'-42')
816 >>> u.setconfig(s, b'int2', b'-42')
810 >>> u.configint(s, b'int2')
817 >>> u.configint(s, b'int2')
811 -42
818 -42
812 >>> u.configint(s, b'unknown', 7)
819 >>> u.configint(s, b'unknown', 7)
813 7
820 7
814 >>> u.setconfig(s, b'invalid', b'somevalue')
821 >>> u.setconfig(s, b'invalid', b'somevalue')
815 >>> u.configint(s, b'invalid')
822 >>> u.configint(s, b'invalid')
816 Traceback (most recent call last):
823 Traceback (most recent call last):
817 ...
824 ...
818 ConfigError: foo.invalid is not a valid integer ('somevalue')
825 ConfigError: foo.invalid is not a valid integer ('somevalue')
819 """
826 """
820
827
821 return self.configwith(
828 return self.configwith(
822 int, section, name, default, b'integer', untrusted
829 int, section, name, default, b'integer', untrusted
823 )
830 )
824
831
825 def configbytes(self, section, name, default=_unset, untrusted=False):
832 def configbytes(self, section, name, default=_unset, untrusted=False):
826 """parse a configuration element as a quantity in bytes
833 """parse a configuration element as a quantity in bytes
827
834
828 Units can be specified as b (bytes), k or kb (kilobytes), m or
835 Units can be specified as b (bytes), k or kb (kilobytes), m or
829 mb (megabytes), g or gb (gigabytes).
836 mb (megabytes), g or gb (gigabytes).
830
837
831 >>> u = ui(); s = b'foo'
838 >>> u = ui(); s = b'foo'
832 >>> u.setconfig(s, b'val1', b'42')
839 >>> u.setconfig(s, b'val1', b'42')
833 >>> u.configbytes(s, b'val1')
840 >>> u.configbytes(s, b'val1')
834 42
841 42
835 >>> u.setconfig(s, b'val2', b'42.5 kb')
842 >>> u.setconfig(s, b'val2', b'42.5 kb')
836 >>> u.configbytes(s, b'val2')
843 >>> u.configbytes(s, b'val2')
837 43520
844 43520
838 >>> u.configbytes(s, b'unknown', b'7 MB')
845 >>> u.configbytes(s, b'unknown', b'7 MB')
839 7340032
846 7340032
840 >>> u.setconfig(s, b'invalid', b'somevalue')
847 >>> u.setconfig(s, b'invalid', b'somevalue')
841 >>> u.configbytes(s, b'invalid')
848 >>> u.configbytes(s, b'invalid')
842 Traceback (most recent call last):
849 Traceback (most recent call last):
843 ...
850 ...
844 ConfigError: foo.invalid is not a byte quantity ('somevalue')
851 ConfigError: foo.invalid is not a byte quantity ('somevalue')
845 """
852 """
846
853
847 value = self._config(section, name, default, untrusted)
854 value = self._config(section, name, default, untrusted)
848 if value is _unset:
855 if value is _unset:
849 if default is _unset:
856 if default is _unset:
850 default = 0
857 default = 0
851 value = default
858 value = default
852 if not isinstance(value, bytes):
859 if not isinstance(value, bytes):
853 return value
860 return value
854 try:
861 try:
855 return util.sizetoint(value)
862 return util.sizetoint(value)
856 except error.ParseError:
863 except error.ParseError:
857 raise error.ConfigError(
864 raise error.ConfigError(
858 _(b"%s.%s is not a byte quantity ('%s')")
865 _(b"%s.%s is not a byte quantity ('%s')")
859 % (section, name, value)
866 % (section, name, value)
860 )
867 )
861
868
862 def configlist(self, section, name, default=_unset, untrusted=False):
869 def configlist(self, section, name, default=_unset, untrusted=False):
863 """parse a configuration element as a list of comma/space separated
870 """parse a configuration element as a list of comma/space separated
864 strings
871 strings
865
872
866 >>> u = ui(); s = b'foo'
873 >>> u = ui(); s = b'foo'
867 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
874 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
868 >>> u.configlist(s, b'list1')
875 >>> u.configlist(s, b'list1')
869 ['this', 'is', 'a small', 'test']
876 ['this', 'is', 'a small', 'test']
870 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
877 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
871 >>> u.configlist(s, b'list2')
878 >>> u.configlist(s, b'list2')
872 ['this', 'is', 'a small', 'test']
879 ['this', 'is', 'a small', 'test']
873 """
880 """
874 # default is not always a list
881 # default is not always a list
875 v = self.configwith(
882 v = self.configwith(
876 config.parselist, section, name, default, b'list', untrusted
883 config.parselist, section, name, default, b'list', untrusted
877 )
884 )
878 if isinstance(v, bytes):
885 if isinstance(v, bytes):
879 return config.parselist(v)
886 return config.parselist(v)
880 elif v is None:
887 elif v is None:
881 return []
888 return []
882 return v
889 return v
883
890
884 def configdate(self, section, name, default=_unset, untrusted=False):
891 def configdate(self, section, name, default=_unset, untrusted=False):
885 """parse a configuration element as a tuple of ints
892 """parse a configuration element as a tuple of ints
886
893
887 >>> u = ui(); s = b'foo'
894 >>> u = ui(); s = b'foo'
888 >>> u.setconfig(s, b'date', b'0 0')
895 >>> u.setconfig(s, b'date', b'0 0')
889 >>> u.configdate(s, b'date')
896 >>> u.configdate(s, b'date')
890 (0, 0)
897 (0, 0)
891 """
898 """
892 if self.config(section, name, default, untrusted):
899 if self.config(section, name, default, untrusted):
893 return self.configwith(
900 return self.configwith(
894 dateutil.parsedate, section, name, default, b'date', untrusted
901 dateutil.parsedate, section, name, default, b'date', untrusted
895 )
902 )
896 if default is _unset:
903 if default is _unset:
897 return None
904 return None
898 return default
905 return default
899
906
900 def configdefault(self, section, name):
907 def configdefault(self, section, name):
901 """returns the default value of the config item"""
908 """returns the default value of the config item"""
902 item = self._knownconfig.get(section, {}).get(name)
909 item = self._knownconfig.get(section, {}).get(name)
903 itemdefault = None
910 itemdefault = None
904 if item is not None:
911 if item is not None:
905 if callable(item.default):
912 if callable(item.default):
906 itemdefault = item.default()
913 itemdefault = item.default()
907 else:
914 else:
908 itemdefault = item.default
915 itemdefault = item.default
909 return itemdefault
916 return itemdefault
910
917
911 def hasconfig(self, section, name, untrusted=False):
918 def hasconfig(self, section, name, untrusted=False):
912 return self._data(untrusted).hasitem(section, name)
919 return self._data(untrusted).hasitem(section, name)
913
920
914 def has_section(self, section, untrusted=False):
921 def has_section(self, section, untrusted=False):
915 '''tell whether section exists in config.'''
922 '''tell whether section exists in config.'''
916 return section in self._data(untrusted)
923 return section in self._data(untrusted)
917
924
918 def configitems(self, section, untrusted=False, ignoresub=False):
925 def configitems(self, section, untrusted=False, ignoresub=False):
919 items = self._data(untrusted).items(section)
926 items = self._data(untrusted).items(section)
920 if ignoresub:
927 if ignoresub:
921 items = [i for i in items if b':' not in i[0]]
928 items = [i for i in items if b':' not in i[0]]
922 if self.debugflag and not untrusted and self._reportuntrusted:
929 if self.debugflag and not untrusted and self._reportuntrusted:
923 for k, v in self._ucfg.items(section):
930 for k, v in self._ucfg.items(section):
924 if self._tcfg.get(section, k) != v:
931 if self._tcfg.get(section, k) != v:
925 self.debug(
932 self.debug(
926 b"ignoring untrusted configuration option "
933 b"ignoring untrusted configuration option "
927 b"%s.%s = %s\n" % (section, k, v)
934 b"%s.%s = %s\n" % (section, k, v)
928 )
935 )
929 return items
936 return items
930
937
931 def walkconfig(self, untrusted=False):
938 def walkconfig(self, untrusted=False):
932 cfg = self._data(untrusted)
939 cfg = self._data(untrusted)
933 for section in cfg.sections():
940 for section in cfg.sections():
934 for name, value in self.configitems(section, untrusted):
941 for name, value in self.configitems(section, untrusted):
935 yield section, name, value
942 yield section, name, value
936
943
937 def plain(self, feature=None):
944 def plain(self, feature=None):
938 """is plain mode active?
945 """is plain mode active?
939
946
940 Plain mode means that all configuration variables which affect
947 Plain mode means that all configuration variables which affect
941 the behavior and output of Mercurial should be
948 the behavior and output of Mercurial should be
942 ignored. Additionally, the output should be stable,
949 ignored. Additionally, the output should be stable,
943 reproducible and suitable for use in scripts or applications.
950 reproducible and suitable for use in scripts or applications.
944
951
945 The only way to trigger plain mode is by setting either the
952 The only way to trigger plain mode is by setting either the
946 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
953 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
947
954
948 The return value can either be
955 The return value can either be
949 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
956 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
950 - False if feature is disabled by default and not included in HGPLAIN
957 - False if feature is disabled by default and not included in HGPLAIN
951 - True otherwise
958 - True otherwise
952 """
959 """
953 if (
960 if (
954 b'HGPLAIN' not in encoding.environ
961 b'HGPLAIN' not in encoding.environ
955 and b'HGPLAINEXCEPT' not in encoding.environ
962 and b'HGPLAINEXCEPT' not in encoding.environ
956 ):
963 ):
957 return False
964 return False
958 exceptions = (
965 exceptions = (
959 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
966 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
960 )
967 )
961 # TODO: add support for HGPLAIN=+feature,-feature syntax
968 # TODO: add support for HGPLAIN=+feature,-feature syntax
962 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
969 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
963 b','
970 b','
964 ):
971 ):
965 exceptions.append(b'strictflags')
972 exceptions.append(b'strictflags')
966 if feature and exceptions:
973 if feature and exceptions:
967 return feature not in exceptions
974 return feature not in exceptions
968 return True
975 return True
969
976
970 def username(self, acceptempty=False):
977 def username(self, acceptempty=False):
971 """Return default username to be used in commits.
978 """Return default username to be used in commits.
972
979
973 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
980 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
974 and stop searching if one of these is set.
981 and stop searching if one of these is set.
975 If not found and acceptempty is True, returns None.
982 If not found and acceptempty is True, returns None.
976 If not found and ui.askusername is True, ask the user, else use
983 If not found and ui.askusername is True, ask the user, else use
977 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
984 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
978 If no username could be found, raise an Abort error.
985 If no username could be found, raise an Abort error.
979 """
986 """
980 user = encoding.environ.get(b"HGUSER")
987 user = encoding.environ.get(b"HGUSER")
981 if user is None:
988 if user is None:
982 user = self.config(b"ui", b"username")
989 user = self.config(b"ui", b"username")
983 if user is not None:
990 if user is not None:
984 user = os.path.expandvars(user)
991 user = os.path.expandvars(user)
985 if user is None:
992 if user is None:
986 user = encoding.environ.get(b"EMAIL")
993 user = encoding.environ.get(b"EMAIL")
987 if user is None and acceptempty:
994 if user is None and acceptempty:
988 return user
995 return user
989 if user is None and self.configbool(b"ui", b"askusername"):
996 if user is None and self.configbool(b"ui", b"askusername"):
990 user = self.prompt(_(b"enter a commit username:"), default=None)
997 user = self.prompt(_(b"enter a commit username:"), default=None)
991 if user is None and not self.interactive():
998 if user is None and not self.interactive():
992 try:
999 try:
993 user = b'%s@%s' % (
1000 user = b'%s@%s' % (
994 procutil.getuser(),
1001 procutil.getuser(),
995 encoding.strtolocal(socket.getfqdn()),
1002 encoding.strtolocal(socket.getfqdn()),
996 )
1003 )
997 self.warn(_(b"no username found, using '%s' instead\n") % user)
1004 self.warn(_(b"no username found, using '%s' instead\n") % user)
998 except KeyError:
1005 except KeyError:
999 pass
1006 pass
1000 if not user:
1007 if not user:
1001 raise error.Abort(
1008 raise error.Abort(
1002 _(b'no username supplied'),
1009 _(b'no username supplied'),
1003 hint=_(b"use 'hg config --edit' " b'to set your username'),
1010 hint=_(b"use 'hg config --edit' " b'to set your username'),
1004 )
1011 )
1005 if b"\n" in user:
1012 if b"\n" in user:
1006 raise error.Abort(
1013 raise error.Abort(
1007 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
1014 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
1008 )
1015 )
1009 return user
1016 return user
1010
1017
1011 def shortuser(self, user):
1018 def shortuser(self, user):
1012 """Return a short representation of a user name or email address."""
1019 """Return a short representation of a user name or email address."""
1013 if not self.verbose:
1020 if not self.verbose:
1014 user = stringutil.shortuser(user)
1021 user = stringutil.shortuser(user)
1015 return user
1022 return user
1016
1023
1017 def expandpath(self, loc, default=None):
1024 def expandpath(self, loc, default=None):
1018 """Return repository location relative to cwd or from [paths]"""
1025 """Return repository location relative to cwd or from [paths]"""
1019 try:
1026 try:
1020 p = self.paths.getpath(loc)
1027 p = self.paths.getpath(loc)
1021 if p:
1028 if p:
1022 return p.rawloc
1029 return p.rawloc
1023 except error.RepoError:
1030 except error.RepoError:
1024 pass
1031 pass
1025
1032
1026 if default:
1033 if default:
1027 try:
1034 try:
1028 p = self.paths.getpath(default)
1035 p = self.paths.getpath(default)
1029 if p:
1036 if p:
1030 return p.rawloc
1037 return p.rawloc
1031 except error.RepoError:
1038 except error.RepoError:
1032 pass
1039 pass
1033
1040
1034 return loc
1041 return loc
1035
1042
1036 @util.propertycache
1043 @util.propertycache
1037 def paths(self):
1044 def paths(self):
1038 return paths(self)
1045 return paths(self)
1039
1046
1040 @property
1047 @property
1041 def fout(self):
1048 def fout(self):
1042 return self._fout
1049 return self._fout
1043
1050
1044 @fout.setter
1051 @fout.setter
1045 def fout(self, f):
1052 def fout(self, f):
1046 self._fout = f
1053 self._fout = f
1047 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1054 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1048
1055
1049 @property
1056 @property
1050 def ferr(self):
1057 def ferr(self):
1051 return self._ferr
1058 return self._ferr
1052
1059
1053 @ferr.setter
1060 @ferr.setter
1054 def ferr(self, f):
1061 def ferr(self, f):
1055 self._ferr = f
1062 self._ferr = f
1056 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1063 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1057
1064
1058 @property
1065 @property
1059 def fin(self):
1066 def fin(self):
1060 return self._fin
1067 return self._fin
1061
1068
1062 @fin.setter
1069 @fin.setter
1063 def fin(self, f):
1070 def fin(self, f):
1064 self._fin = f
1071 self._fin = f
1065
1072
1066 @property
1073 @property
1067 def fmsg(self):
1074 def fmsg(self):
1068 """Stream dedicated for status/error messages; may be None if
1075 """Stream dedicated for status/error messages; may be None if
1069 fout/ferr are used"""
1076 fout/ferr are used"""
1070 return self._fmsg
1077 return self._fmsg
1071
1078
1072 @fmsg.setter
1079 @fmsg.setter
1073 def fmsg(self, f):
1080 def fmsg(self, f):
1074 self._fmsg = f
1081 self._fmsg = f
1075 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1082 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1076
1083
1077 def pushbuffer(self, error=False, subproc=False, labeled=False):
1084 def pushbuffer(self, error=False, subproc=False, labeled=False):
1078 """install a buffer to capture standard output of the ui object
1085 """install a buffer to capture standard output of the ui object
1079
1086
1080 If error is True, the error output will be captured too.
1087 If error is True, the error output will be captured too.
1081
1088
1082 If subproc is True, output from subprocesses (typically hooks) will be
1089 If subproc is True, output from subprocesses (typically hooks) will be
1083 captured too.
1090 captured too.
1084
1091
1085 If labeled is True, any labels associated with buffered
1092 If labeled is True, any labels associated with buffered
1086 output will be handled. By default, this has no effect
1093 output will be handled. By default, this has no effect
1087 on the output returned, but extensions and GUI tools may
1094 on the output returned, but extensions and GUI tools may
1088 handle this argument and returned styled output. If output
1095 handle this argument and returned styled output. If output
1089 is being buffered so it can be captured and parsed or
1096 is being buffered so it can be captured and parsed or
1090 processed, labeled should not be set to True.
1097 processed, labeled should not be set to True.
1091 """
1098 """
1092 self._buffers.append([])
1099 self._buffers.append([])
1093 self._bufferstates.append((error, subproc, labeled))
1100 self._bufferstates.append((error, subproc, labeled))
1094 self._bufferapplylabels = labeled
1101 self._bufferapplylabels = labeled
1095
1102
1096 def popbuffer(self):
1103 def popbuffer(self):
1097 '''pop the last buffer and return the buffered output'''
1104 '''pop the last buffer and return the buffered output'''
1098 self._bufferstates.pop()
1105 self._bufferstates.pop()
1099 if self._bufferstates:
1106 if self._bufferstates:
1100 self._bufferapplylabels = self._bufferstates[-1][2]
1107 self._bufferapplylabels = self._bufferstates[-1][2]
1101 else:
1108 else:
1102 self._bufferapplylabels = None
1109 self._bufferapplylabels = None
1103
1110
1104 return b"".join(self._buffers.pop())
1111 return b"".join(self._buffers.pop())
1105
1112
1106 def _isbuffered(self, dest):
1113 def _isbuffered(self, dest):
1107 if dest is self._fout:
1114 if dest is self._fout:
1108 return bool(self._buffers)
1115 return bool(self._buffers)
1109 if dest is self._ferr:
1116 if dest is self._ferr:
1110 return bool(self._bufferstates and self._bufferstates[-1][0])
1117 return bool(self._bufferstates and self._bufferstates[-1][0])
1111 return False
1118 return False
1112
1119
1113 def canwritewithoutlabels(self):
1120 def canwritewithoutlabels(self):
1114 '''check if write skips the label'''
1121 '''check if write skips the label'''
1115 if self._buffers and not self._bufferapplylabels:
1122 if self._buffers and not self._bufferapplylabels:
1116 return True
1123 return True
1117 return self._colormode is None
1124 return self._colormode is None
1118
1125
1119 def canbatchlabeledwrites(self):
1126 def canbatchlabeledwrites(self):
1120 '''check if write calls with labels are batchable'''
1127 '''check if write calls with labels are batchable'''
1121 # Windows color printing is special, see ``write``.
1128 # Windows color printing is special, see ``write``.
1122 return self._colormode != b'win32'
1129 return self._colormode != b'win32'
1123
1130
1124 def write(self, *args, **opts):
1131 def write(self, *args, **opts):
1125 """write args to output
1132 """write args to output
1126
1133
1127 By default, this method simply writes to the buffer or stdout.
1134 By default, this method simply writes to the buffer or stdout.
1128 Color mode can be set on the UI class to have the output decorated
1135 Color mode can be set on the UI class to have the output decorated
1129 with color modifier before being written to stdout.
1136 with color modifier before being written to stdout.
1130
1137
1131 The color used is controlled by an optional keyword argument, "label".
1138 The color used is controlled by an optional keyword argument, "label".
1132 This should be a string containing label names separated by space.
1139 This should be a string containing label names separated by space.
1133 Label names take the form of "topic.type". For example, ui.debug()
1140 Label names take the form of "topic.type". For example, ui.debug()
1134 issues a label of "ui.debug".
1141 issues a label of "ui.debug".
1135
1142
1136 Progress reports via stderr are normally cleared before writing as
1143 Progress reports via stderr are normally cleared before writing as
1137 stdout and stderr go to the same terminal. This can be skipped with
1144 stdout and stderr go to the same terminal. This can be skipped with
1138 the optional keyword argument "keepprogressbar". The progress bar
1145 the optional keyword argument "keepprogressbar". The progress bar
1139 will continue to occupy a partial line on stderr in that case.
1146 will continue to occupy a partial line on stderr in that case.
1140 This functionality is intended when Mercurial acts as data source
1147 This functionality is intended when Mercurial acts as data source
1141 in a pipe.
1148 in a pipe.
1142
1149
1143 When labeling output for a specific command, a label of
1150 When labeling output for a specific command, a label of
1144 "cmdname.type" is recommended. For example, status issues
1151 "cmdname.type" is recommended. For example, status issues
1145 a label of "status.modified" for modified files.
1152 a label of "status.modified" for modified files.
1146 """
1153 """
1147 dest = self._fout
1154 dest = self._fout
1148
1155
1149 # inlined _write() for speed
1156 # inlined _write() for speed
1150 if self._buffers:
1157 if self._buffers:
1151 label = opts.get('label', b'')
1158 label = opts.get('label', b'')
1152 if label and self._bufferapplylabels:
1159 if label and self._bufferapplylabels:
1153 self._buffers[-1].extend(self.label(a, label) for a in args)
1160 self._buffers[-1].extend(self.label(a, label) for a in args)
1154 else:
1161 else:
1155 self._buffers[-1].extend(args)
1162 self._buffers[-1].extend(args)
1156 return
1163 return
1157
1164
1158 # inlined _writenobuf() for speed
1165 # inlined _writenobuf() for speed
1159 if not opts.get('keepprogressbar', False):
1166 if not opts.get('keepprogressbar', False):
1160 self._progclear()
1167 self._progclear()
1161 msg = b''.join(args)
1168 msg = b''.join(args)
1162
1169
1163 # opencode timeblockedsection because this is a critical path
1170 # opencode timeblockedsection because this is a critical path
1164 starttime = util.timer()
1171 starttime = util.timer()
1165 try:
1172 try:
1166 if self._colormode == b'win32':
1173 if self._colormode == b'win32':
1167 # windows color printing is its own can of crab, defer to
1174 # windows color printing is its own can of crab, defer to
1168 # the color module and that is it.
1175 # the color module and that is it.
1169 color.win32print(self, dest.write, msg, **opts)
1176 color.win32print(self, dest.write, msg, **opts)
1170 else:
1177 else:
1171 if self._colormode is not None:
1178 if self._colormode is not None:
1172 label = opts.get('label', b'')
1179 label = opts.get('label', b'')
1173 msg = self.label(msg, label)
1180 msg = self.label(msg, label)
1174 dest.write(msg)
1181 dest.write(msg)
1175 except IOError as err:
1182 except IOError as err:
1176 raise error.StdioError(err)
1183 raise error.StdioError(err)
1177 finally:
1184 finally:
1178 self._blockedtimes[b'stdio_blocked'] += (
1185 self._blockedtimes[b'stdio_blocked'] += (
1179 util.timer() - starttime
1186 util.timer() - starttime
1180 ) * 1000
1187 ) * 1000
1181
1188
1182 def write_err(self, *args, **opts):
1189 def write_err(self, *args, **opts):
1183 self._write(self._ferr, *args, **opts)
1190 self._write(self._ferr, *args, **opts)
1184
1191
1185 def _write(self, dest, *args, **opts):
1192 def _write(self, dest, *args, **opts):
1186 # update write() as well if you touch this code
1193 # update write() as well if you touch this code
1187 if self._isbuffered(dest):
1194 if self._isbuffered(dest):
1188 label = opts.get('label', b'')
1195 label = opts.get('label', b'')
1189 if label and self._bufferapplylabels:
1196 if label and self._bufferapplylabels:
1190 self._buffers[-1].extend(self.label(a, label) for a in args)
1197 self._buffers[-1].extend(self.label(a, label) for a in args)
1191 else:
1198 else:
1192 self._buffers[-1].extend(args)
1199 self._buffers[-1].extend(args)
1193 else:
1200 else:
1194 self._writenobuf(dest, *args, **opts)
1201 self._writenobuf(dest, *args, **opts)
1195
1202
1196 def _writenobuf(self, dest, *args, **opts):
1203 def _writenobuf(self, dest, *args, **opts):
1197 # update write() as well if you touch this code
1204 # update write() as well if you touch this code
1198 if not opts.get('keepprogressbar', False):
1205 if not opts.get('keepprogressbar', False):
1199 self._progclear()
1206 self._progclear()
1200 msg = b''.join(args)
1207 msg = b''.join(args)
1201
1208
1202 # opencode timeblockedsection because this is a critical path
1209 # opencode timeblockedsection because this is a critical path
1203 starttime = util.timer()
1210 starttime = util.timer()
1204 try:
1211 try:
1205 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1212 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1206 self._fout.flush()
1213 self._fout.flush()
1207 if getattr(dest, 'structured', False):
1214 if getattr(dest, 'structured', False):
1208 # channel for machine-readable output with metadata, where
1215 # channel for machine-readable output with metadata, where
1209 # no extra colorization is necessary.
1216 # no extra colorization is necessary.
1210 dest.write(msg, **opts)
1217 dest.write(msg, **opts)
1211 elif self._colormode == b'win32':
1218 elif self._colormode == b'win32':
1212 # windows color printing is its own can of crab, defer to
1219 # windows color printing is its own can of crab, defer to
1213 # the color module and that is it.
1220 # the color module and that is it.
1214 color.win32print(self, dest.write, msg, **opts)
1221 color.win32print(self, dest.write, msg, **opts)
1215 else:
1222 else:
1216 if self._colormode is not None:
1223 if self._colormode is not None:
1217 label = opts.get('label', b'')
1224 label = opts.get('label', b'')
1218 msg = self.label(msg, label)
1225 msg = self.label(msg, label)
1219 dest.write(msg)
1226 dest.write(msg)
1220 # stderr may be buffered under win32 when redirected to files,
1227 # stderr may be buffered under win32 when redirected to files,
1221 # including stdout.
1228 # including stdout.
1222 if dest is self._ferr and not getattr(dest, 'closed', False):
1229 if dest is self._ferr and not getattr(dest, 'closed', False):
1223 dest.flush()
1230 dest.flush()
1224 except IOError as err:
1231 except IOError as err:
1225 if dest is self._ferr and err.errno in (
1232 if dest is self._ferr and err.errno in (
1226 errno.EPIPE,
1233 errno.EPIPE,
1227 errno.EIO,
1234 errno.EIO,
1228 errno.EBADF,
1235 errno.EBADF,
1229 ):
1236 ):
1230 # no way to report the error, so ignore it
1237 # no way to report the error, so ignore it
1231 return
1238 return
1232 raise error.StdioError(err)
1239 raise error.StdioError(err)
1233 finally:
1240 finally:
1234 self._blockedtimes[b'stdio_blocked'] += (
1241 self._blockedtimes[b'stdio_blocked'] += (
1235 util.timer() - starttime
1242 util.timer() - starttime
1236 ) * 1000
1243 ) * 1000
1237
1244
1238 def _writemsg(self, dest, *args, **opts):
1245 def _writemsg(self, dest, *args, **opts):
1239 timestamp = self.showtimestamp and opts.get('type') in {
1246 timestamp = self.showtimestamp and opts.get('type') in {
1240 b'debug',
1247 b'debug',
1241 b'error',
1248 b'error',
1242 b'note',
1249 b'note',
1243 b'status',
1250 b'status',
1244 b'warning',
1251 b'warning',
1245 }
1252 }
1246 if timestamp:
1253 if timestamp:
1247 args = (
1254 args = (
1248 b'[%s] '
1255 b'[%s] '
1249 % pycompat.bytestr(datetime.datetime.now().isoformat()),
1256 % pycompat.bytestr(datetime.datetime.now().isoformat()),
1250 ) + args
1257 ) + args
1251 _writemsgwith(self._write, dest, *args, **opts)
1258 _writemsgwith(self._write, dest, *args, **opts)
1252 if timestamp:
1259 if timestamp:
1253 dest.flush()
1260 dest.flush()
1254
1261
1255 def _writemsgnobuf(self, dest, *args, **opts):
1262 def _writemsgnobuf(self, dest, *args, **opts):
1256 _writemsgwith(self._writenobuf, dest, *args, **opts)
1263 _writemsgwith(self._writenobuf, dest, *args, **opts)
1257
1264
1258 def flush(self):
1265 def flush(self):
1259 # opencode timeblockedsection because this is a critical path
1266 # opencode timeblockedsection because this is a critical path
1260 starttime = util.timer()
1267 starttime = util.timer()
1261 try:
1268 try:
1262 try:
1269 try:
1263 self._fout.flush()
1270 self._fout.flush()
1264 except IOError as err:
1271 except IOError as err:
1265 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1272 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1266 raise error.StdioError(err)
1273 raise error.StdioError(err)
1267 finally:
1274 finally:
1268 try:
1275 try:
1269 self._ferr.flush()
1276 self._ferr.flush()
1270 except IOError as err:
1277 except IOError as err:
1271 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1278 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1272 raise error.StdioError(err)
1279 raise error.StdioError(err)
1273 finally:
1280 finally:
1274 self._blockedtimes[b'stdio_blocked'] += (
1281 self._blockedtimes[b'stdio_blocked'] += (
1275 util.timer() - starttime
1282 util.timer() - starttime
1276 ) * 1000
1283 ) * 1000
1277
1284
1278 def _isatty(self, fh):
1285 def _isatty(self, fh):
1279 if self.configbool(b'ui', b'nontty'):
1286 if self.configbool(b'ui', b'nontty'):
1280 return False
1287 return False
1281 return procutil.isatty(fh)
1288 return procutil.isatty(fh)
1282
1289
1283 def protectfinout(self):
1290 def protectfinout(self):
1284 """Duplicate ui streams and redirect original if they are stdio
1291 """Duplicate ui streams and redirect original if they are stdio
1285
1292
1286 Returns (fin, fout) which point to the original ui fds, but may be
1293 Returns (fin, fout) which point to the original ui fds, but may be
1287 copy of them. The returned streams can be considered "owned" in that
1294 copy of them. The returned streams can be considered "owned" in that
1288 print(), exec(), etc. never reach to them.
1295 print(), exec(), etc. never reach to them.
1289 """
1296 """
1290 if self._finoutredirected:
1297 if self._finoutredirected:
1291 # if already redirected, protectstdio() would just create another
1298 # if already redirected, protectstdio() would just create another
1292 # nullfd pair, which is equivalent to returning self._fin/_fout.
1299 # nullfd pair, which is equivalent to returning self._fin/_fout.
1293 return self._fin, self._fout
1300 return self._fin, self._fout
1294 fin, fout = procutil.protectstdio(self._fin, self._fout)
1301 fin, fout = procutil.protectstdio(self._fin, self._fout)
1295 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1302 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1296 return fin, fout
1303 return fin, fout
1297
1304
1298 def restorefinout(self, fin, fout):
1305 def restorefinout(self, fin, fout):
1299 """Restore ui streams from possibly duplicated (fin, fout)"""
1306 """Restore ui streams from possibly duplicated (fin, fout)"""
1300 if (fin, fout) == (self._fin, self._fout):
1307 if (fin, fout) == (self._fin, self._fout):
1301 return
1308 return
1302 procutil.restorestdio(self._fin, self._fout, fin, fout)
1309 procutil.restorestdio(self._fin, self._fout, fin, fout)
1303 # protectfinout() won't create more than one duplicated streams,
1310 # protectfinout() won't create more than one duplicated streams,
1304 # so we can just turn the redirection flag off.
1311 # so we can just turn the redirection flag off.
1305 self._finoutredirected = False
1312 self._finoutredirected = False
1306
1313
1307 @contextlib.contextmanager
1314 @contextlib.contextmanager
1308 def protectedfinout(self):
1315 def protectedfinout(self):
1309 """Run code block with protected standard streams"""
1316 """Run code block with protected standard streams"""
1310 fin, fout = self.protectfinout()
1317 fin, fout = self.protectfinout()
1311 try:
1318 try:
1312 yield fin, fout
1319 yield fin, fout
1313 finally:
1320 finally:
1314 self.restorefinout(fin, fout)
1321 self.restorefinout(fin, fout)
1315
1322
1316 def disablepager(self):
1323 def disablepager(self):
1317 self._disablepager = True
1324 self._disablepager = True
1318
1325
1319 def pager(self, command):
1326 def pager(self, command):
1320 """Start a pager for subsequent command output.
1327 """Start a pager for subsequent command output.
1321
1328
1322 Commands which produce a long stream of output should call
1329 Commands which produce a long stream of output should call
1323 this function to activate the user's preferred pagination
1330 this function to activate the user's preferred pagination
1324 mechanism (which may be no pager). Calling this function
1331 mechanism (which may be no pager). Calling this function
1325 precludes any future use of interactive functionality, such as
1332 precludes any future use of interactive functionality, such as
1326 prompting the user or activating curses.
1333 prompting the user or activating curses.
1327
1334
1328 Args:
1335 Args:
1329 command: The full, non-aliased name of the command. That is, "log"
1336 command: The full, non-aliased name of the command. That is, "log"
1330 not "history, "summary" not "summ", etc.
1337 not "history, "summary" not "summ", etc.
1331 """
1338 """
1332 if self._disablepager or self.pageractive:
1339 if self._disablepager or self.pageractive:
1333 # how pager should do is already determined
1340 # how pager should do is already determined
1334 return
1341 return
1335
1342
1336 if not command.startswith(b'internal-always-') and (
1343 if not command.startswith(b'internal-always-') and (
1337 # explicit --pager=on (= 'internal-always-' prefix) should
1344 # explicit --pager=on (= 'internal-always-' prefix) should
1338 # take precedence over disabling factors below
1345 # take precedence over disabling factors below
1339 command in self.configlist(b'pager', b'ignore')
1346 command in self.configlist(b'pager', b'ignore')
1340 or not self.configbool(b'ui', b'paginate')
1347 or not self.configbool(b'ui', b'paginate')
1341 or not self.configbool(b'pager', b'attend-' + command, True)
1348 or not self.configbool(b'pager', b'attend-' + command, True)
1342 or encoding.environ.get(b'TERM') == b'dumb'
1349 or encoding.environ.get(b'TERM') == b'dumb'
1343 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1350 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1344 # formatted() will need some adjustment.
1351 # formatted() will need some adjustment.
1345 or not self.formatted()
1352 or not self.formatted()
1346 or self.plain()
1353 or self.plain()
1347 or self._buffers
1354 or self._buffers
1348 # TODO: expose debugger-enabled on the UI object
1355 # TODO: expose debugger-enabled on the UI object
1349 or b'--debugger' in pycompat.sysargv
1356 or b'--debugger' in pycompat.sysargv
1350 ):
1357 ):
1351 # We only want to paginate if the ui appears to be
1358 # We only want to paginate if the ui appears to be
1352 # interactive, the user didn't say HGPLAIN or
1359 # interactive, the user didn't say HGPLAIN or
1353 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1360 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1354 return
1361 return
1355
1362
1356 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1363 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1357 if not pagercmd:
1364 if not pagercmd:
1358 return
1365 return
1359
1366
1360 pagerenv = {}
1367 pagerenv = {}
1361 for name, value in rcutil.defaultpagerenv().items():
1368 for name, value in rcutil.defaultpagerenv().items():
1362 if name not in encoding.environ:
1369 if name not in encoding.environ:
1363 pagerenv[name] = value
1370 pagerenv[name] = value
1364
1371
1365 self.debug(
1372 self.debug(
1366 b'starting pager for command %s\n' % stringutil.pprint(command)
1373 b'starting pager for command %s\n' % stringutil.pprint(command)
1367 )
1374 )
1368 self.flush()
1375 self.flush()
1369
1376
1370 wasformatted = self.formatted()
1377 wasformatted = self.formatted()
1371 if util.safehasattr(signal, b"SIGPIPE"):
1378 if util.safehasattr(signal, b"SIGPIPE"):
1372 signal.signal(signal.SIGPIPE, _catchterm)
1379 signal.signal(signal.SIGPIPE, _catchterm)
1373 if self._runpager(pagercmd, pagerenv):
1380 if self._runpager(pagercmd, pagerenv):
1374 self.pageractive = True
1381 self.pageractive = True
1375 # Preserve the formatted-ness of the UI. This is important
1382 # Preserve the formatted-ness of the UI. This is important
1376 # because we mess with stdout, which might confuse
1383 # because we mess with stdout, which might confuse
1377 # auto-detection of things being formatted.
1384 # auto-detection of things being formatted.
1378 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1385 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1379 self.setconfig(b'ui', b'interactive', False, b'pager')
1386 self.setconfig(b'ui', b'interactive', False, b'pager')
1380
1387
1381 # If pagermode differs from color.mode, reconfigure color now that
1388 # If pagermode differs from color.mode, reconfigure color now that
1382 # pageractive is set.
1389 # pageractive is set.
1383 cm = self._colormode
1390 cm = self._colormode
1384 if cm != self.config(b'color', b'pagermode', cm):
1391 if cm != self.config(b'color', b'pagermode', cm):
1385 color.setup(self)
1392 color.setup(self)
1386 else:
1393 else:
1387 # If the pager can't be spawned in dispatch when --pager=on is
1394 # If the pager can't be spawned in dispatch when --pager=on is
1388 # given, don't try again when the command runs, to avoid a duplicate
1395 # given, don't try again when the command runs, to avoid a duplicate
1389 # warning about a missing pager command.
1396 # warning about a missing pager command.
1390 self.disablepager()
1397 self.disablepager()
1391
1398
1392 def _runpager(self, command, env=None):
1399 def _runpager(self, command, env=None):
1393 """Actually start the pager and set up file descriptors.
1400 """Actually start the pager and set up file descriptors.
1394
1401
1395 This is separate in part so that extensions (like chg) can
1402 This is separate in part so that extensions (like chg) can
1396 override how a pager is invoked.
1403 override how a pager is invoked.
1397 """
1404 """
1398 if command == b'cat':
1405 if command == b'cat':
1399 # Save ourselves some work.
1406 # Save ourselves some work.
1400 return False
1407 return False
1401 # If the command doesn't contain any of these characters, we
1408 # If the command doesn't contain any of these characters, we
1402 # assume it's a binary and exec it directly. This means for
1409 # assume it's a binary and exec it directly. This means for
1403 # simple pager command configurations, we can degrade
1410 # simple pager command configurations, we can degrade
1404 # gracefully and tell the user about their broken pager.
1411 # gracefully and tell the user about their broken pager.
1405 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1412 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1406
1413
1407 if pycompat.iswindows and not shell:
1414 if pycompat.iswindows and not shell:
1408 # Window's built-in `more` cannot be invoked with shell=False, but
1415 # Window's built-in `more` cannot be invoked with shell=False, but
1409 # its `more.com` can. Hide this implementation detail from the
1416 # its `more.com` can. Hide this implementation detail from the
1410 # user so we can also get sane bad PAGER behavior. MSYS has
1417 # user so we can also get sane bad PAGER behavior. MSYS has
1411 # `more.exe`, so do a cmd.exe style resolution of the executable to
1418 # `more.exe`, so do a cmd.exe style resolution of the executable to
1412 # determine which one to use.
1419 # determine which one to use.
1413 fullcmd = procutil.findexe(command)
1420 fullcmd = procutil.findexe(command)
1414 if not fullcmd:
1421 if not fullcmd:
1415 self.warn(
1422 self.warn(
1416 _(b"missing pager command '%s', skipping pager\n") % command
1423 _(b"missing pager command '%s', skipping pager\n") % command
1417 )
1424 )
1418 return False
1425 return False
1419
1426
1420 command = fullcmd
1427 command = fullcmd
1421
1428
1422 try:
1429 try:
1423 pager = subprocess.Popen(
1430 pager = subprocess.Popen(
1424 procutil.tonativestr(command),
1431 procutil.tonativestr(command),
1425 shell=shell,
1432 shell=shell,
1426 bufsize=-1,
1433 bufsize=-1,
1427 close_fds=procutil.closefds,
1434 close_fds=procutil.closefds,
1428 stdin=subprocess.PIPE,
1435 stdin=subprocess.PIPE,
1429 stdout=procutil.stdout,
1436 stdout=procutil.stdout,
1430 stderr=procutil.stderr,
1437 stderr=procutil.stderr,
1431 env=procutil.tonativeenv(procutil.shellenviron(env)),
1438 env=procutil.tonativeenv(procutil.shellenviron(env)),
1432 )
1439 )
1433 except OSError as e:
1440 except OSError as e:
1434 if e.errno == errno.ENOENT and not shell:
1441 if e.errno == errno.ENOENT and not shell:
1435 self.warn(
1442 self.warn(
1436 _(b"missing pager command '%s', skipping pager\n") % command
1443 _(b"missing pager command '%s', skipping pager\n") % command
1437 )
1444 )
1438 return False
1445 return False
1439 raise
1446 raise
1440
1447
1441 # back up original file descriptors
1448 # back up original file descriptors
1442 stdoutfd = os.dup(procutil.stdout.fileno())
1449 stdoutfd = os.dup(procutil.stdout.fileno())
1443 stderrfd = os.dup(procutil.stderr.fileno())
1450 stderrfd = os.dup(procutil.stderr.fileno())
1444
1451
1445 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1452 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1446 if self._isatty(procutil.stderr):
1453 if self._isatty(procutil.stderr):
1447 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1454 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1448
1455
1449 @self.atexit
1456 @self.atexit
1450 def killpager():
1457 def killpager():
1451 if util.safehasattr(signal, b"SIGINT"):
1458 if util.safehasattr(signal, b"SIGINT"):
1452 signal.signal(signal.SIGINT, signal.SIG_IGN)
1459 signal.signal(signal.SIGINT, signal.SIG_IGN)
1453 # restore original fds, closing pager.stdin copies in the process
1460 # restore original fds, closing pager.stdin copies in the process
1454 os.dup2(stdoutfd, procutil.stdout.fileno())
1461 os.dup2(stdoutfd, procutil.stdout.fileno())
1455 os.dup2(stderrfd, procutil.stderr.fileno())
1462 os.dup2(stderrfd, procutil.stderr.fileno())
1456 pager.stdin.close()
1463 pager.stdin.close()
1457 pager.wait()
1464 pager.wait()
1458
1465
1459 return True
1466 return True
1460
1467
1461 @property
1468 @property
1462 def _exithandlers(self):
1469 def _exithandlers(self):
1463 return _reqexithandlers
1470 return _reqexithandlers
1464
1471
1465 def atexit(self, func, *args, **kwargs):
1472 def atexit(self, func, *args, **kwargs):
1466 """register a function to run after dispatching a request
1473 """register a function to run after dispatching a request
1467
1474
1468 Handlers do not stay registered across request boundaries."""
1475 Handlers do not stay registered across request boundaries."""
1469 self._exithandlers.append((func, args, kwargs))
1476 self._exithandlers.append((func, args, kwargs))
1470 return func
1477 return func
1471
1478
1472 def interface(self, feature):
1479 def interface(self, feature):
1473 """what interface to use for interactive console features?
1480 """what interface to use for interactive console features?
1474
1481
1475 The interface is controlled by the value of `ui.interface` but also by
1482 The interface is controlled by the value of `ui.interface` but also by
1476 the value of feature-specific configuration. For example:
1483 the value of feature-specific configuration. For example:
1477
1484
1478 ui.interface.histedit = text
1485 ui.interface.histedit = text
1479 ui.interface.chunkselector = curses
1486 ui.interface.chunkselector = curses
1480
1487
1481 Here the features are "histedit" and "chunkselector".
1488 Here the features are "histedit" and "chunkselector".
1482
1489
1483 The configuration above means that the default interfaces for commands
1490 The configuration above means that the default interfaces for commands
1484 is curses, the interface for histedit is text and the interface for
1491 is curses, the interface for histedit is text and the interface for
1485 selecting chunk is crecord (the best curses interface available).
1492 selecting chunk is crecord (the best curses interface available).
1486
1493
1487 Consider the following example:
1494 Consider the following example:
1488 ui.interface = curses
1495 ui.interface = curses
1489 ui.interface.histedit = text
1496 ui.interface.histedit = text
1490
1497
1491 Then histedit will use the text interface and chunkselector will use
1498 Then histedit will use the text interface and chunkselector will use
1492 the default curses interface (crecord at the moment).
1499 the default curses interface (crecord at the moment).
1493 """
1500 """
1494 alldefaults = frozenset([b"text", b"curses"])
1501 alldefaults = frozenset([b"text", b"curses"])
1495
1502
1496 featureinterfaces = {
1503 featureinterfaces = {
1497 b"chunkselector": [
1504 b"chunkselector": [
1498 b"text",
1505 b"text",
1499 b"curses",
1506 b"curses",
1500 ],
1507 ],
1501 b"histedit": [
1508 b"histedit": [
1502 b"text",
1509 b"text",
1503 b"curses",
1510 b"curses",
1504 ],
1511 ],
1505 }
1512 }
1506
1513
1507 # Feature-specific interface
1514 # Feature-specific interface
1508 if feature not in featureinterfaces.keys():
1515 if feature not in featureinterfaces.keys():
1509 # Programming error, not user error
1516 # Programming error, not user error
1510 raise ValueError(b"Unknown feature requested %s" % feature)
1517 raise ValueError(b"Unknown feature requested %s" % feature)
1511
1518
1512 availableinterfaces = frozenset(featureinterfaces[feature])
1519 availableinterfaces = frozenset(featureinterfaces[feature])
1513 if alldefaults > availableinterfaces:
1520 if alldefaults > availableinterfaces:
1514 # Programming error, not user error. We need a use case to
1521 # Programming error, not user error. We need a use case to
1515 # define the right thing to do here.
1522 # define the right thing to do here.
1516 raise ValueError(
1523 raise ValueError(
1517 b"Feature %s does not handle all default interfaces" % feature
1524 b"Feature %s does not handle all default interfaces" % feature
1518 )
1525 )
1519
1526
1520 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1527 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1521 return b"text"
1528 return b"text"
1522
1529
1523 # Default interface for all the features
1530 # Default interface for all the features
1524 defaultinterface = b"text"
1531 defaultinterface = b"text"
1525 i = self.config(b"ui", b"interface")
1532 i = self.config(b"ui", b"interface")
1526 if i in alldefaults:
1533 if i in alldefaults:
1527 defaultinterface = i
1534 defaultinterface = i
1528
1535
1529 choseninterface = defaultinterface
1536 choseninterface = defaultinterface
1530 f = self.config(b"ui", b"interface.%s" % feature)
1537 f = self.config(b"ui", b"interface.%s" % feature)
1531 if f in availableinterfaces:
1538 if f in availableinterfaces:
1532 choseninterface = f
1539 choseninterface = f
1533
1540
1534 if i is not None and defaultinterface != i:
1541 if i is not None and defaultinterface != i:
1535 if f is not None:
1542 if f is not None:
1536 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1543 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1537 else:
1544 else:
1538 self.warn(
1545 self.warn(
1539 _(b"invalid value for ui.interface: %s (using %s)\n")
1546 _(b"invalid value for ui.interface: %s (using %s)\n")
1540 % (i, choseninterface)
1547 % (i, choseninterface)
1541 )
1548 )
1542 if f is not None and choseninterface != f:
1549 if f is not None and choseninterface != f:
1543 self.warn(
1550 self.warn(
1544 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1551 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1545 % (feature, f, choseninterface)
1552 % (feature, f, choseninterface)
1546 )
1553 )
1547
1554
1548 return choseninterface
1555 return choseninterface
1549
1556
1550 def interactive(self):
1557 def interactive(self):
1551 """is interactive input allowed?
1558 """is interactive input allowed?
1552
1559
1553 An interactive session is a session where input can be reasonably read
1560 An interactive session is a session where input can be reasonably read
1554 from `sys.stdin'. If this function returns false, any attempt to read
1561 from `sys.stdin'. If this function returns false, any attempt to read
1555 from stdin should fail with an error, unless a sensible default has been
1562 from stdin should fail with an error, unless a sensible default has been
1556 specified.
1563 specified.
1557
1564
1558 Interactiveness is triggered by the value of the `ui.interactive'
1565 Interactiveness is triggered by the value of the `ui.interactive'
1559 configuration variable or - if it is unset - when `sys.stdin' points
1566 configuration variable or - if it is unset - when `sys.stdin' points
1560 to a terminal device.
1567 to a terminal device.
1561
1568
1562 This function refers to input only; for output, see `ui.formatted()'.
1569 This function refers to input only; for output, see `ui.formatted()'.
1563 """
1570 """
1564 i = self.configbool(b"ui", b"interactive")
1571 i = self.configbool(b"ui", b"interactive")
1565 if i is None:
1572 if i is None:
1566 # some environments replace stdin without implementing isatty
1573 # some environments replace stdin without implementing isatty
1567 # usually those are non-interactive
1574 # usually those are non-interactive
1568 return self._isatty(self._fin)
1575 return self._isatty(self._fin)
1569
1576
1570 return i
1577 return i
1571
1578
1572 def termwidth(self):
1579 def termwidth(self):
1573 """how wide is the terminal in columns?"""
1580 """how wide is the terminal in columns?"""
1574 if b'COLUMNS' in encoding.environ:
1581 if b'COLUMNS' in encoding.environ:
1575 try:
1582 try:
1576 return int(encoding.environ[b'COLUMNS'])
1583 return int(encoding.environ[b'COLUMNS'])
1577 except ValueError:
1584 except ValueError:
1578 pass
1585 pass
1579 return scmutil.termsize(self)[0]
1586 return scmutil.termsize(self)[0]
1580
1587
1581 def formatted(self):
1588 def formatted(self):
1582 """should formatted output be used?
1589 """should formatted output be used?
1583
1590
1584 It is often desirable to format the output to suite the output medium.
1591 It is often desirable to format the output to suite the output medium.
1585 Examples of this are truncating long lines or colorizing messages.
1592 Examples of this are truncating long lines or colorizing messages.
1586 However, this is not often not desirable when piping output into other
1593 However, this is not often not desirable when piping output into other
1587 utilities, e.g. `grep'.
1594 utilities, e.g. `grep'.
1588
1595
1589 Formatted output is triggered by the value of the `ui.formatted'
1596 Formatted output is triggered by the value of the `ui.formatted'
1590 configuration variable or - if it is unset - when `sys.stdout' points
1597 configuration variable or - if it is unset - when `sys.stdout' points
1591 to a terminal device. Please note that `ui.formatted' should be
1598 to a terminal device. Please note that `ui.formatted' should be
1592 considered an implementation detail; it is not intended for use outside
1599 considered an implementation detail; it is not intended for use outside
1593 Mercurial or its extensions.
1600 Mercurial or its extensions.
1594
1601
1595 This function refers to output only; for input, see `ui.interactive()'.
1602 This function refers to output only; for input, see `ui.interactive()'.
1596 This function always returns false when in plain mode, see `ui.plain()'.
1603 This function always returns false when in plain mode, see `ui.plain()'.
1597 """
1604 """
1598 if self.plain():
1605 if self.plain():
1599 return False
1606 return False
1600
1607
1601 i = self.configbool(b"ui", b"formatted")
1608 i = self.configbool(b"ui", b"formatted")
1602 if i is None:
1609 if i is None:
1603 # some environments replace stdout without implementing isatty
1610 # some environments replace stdout without implementing isatty
1604 # usually those are non-interactive
1611 # usually those are non-interactive
1605 return self._isatty(self._fout)
1612 return self._isatty(self._fout)
1606
1613
1607 return i
1614 return i
1608
1615
1609 def _readline(self, prompt=b' ', promptopts=None):
1616 def _readline(self, prompt=b' ', promptopts=None):
1610 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1617 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1611 # because they have to be text streams with *no buffering*. Instead,
1618 # because they have to be text streams with *no buffering*. Instead,
1612 # we use rawinput() only if call_readline() will be invoked by
1619 # we use rawinput() only if call_readline() will be invoked by
1613 # PyOS_Readline(), so no I/O will be made at Python layer.
1620 # PyOS_Readline(), so no I/O will be made at Python layer.
1614 usereadline = (
1621 usereadline = (
1615 self._isatty(self._fin)
1622 self._isatty(self._fin)
1616 and self._isatty(self._fout)
1623 and self._isatty(self._fout)
1617 and procutil.isstdin(self._fin)
1624 and procutil.isstdin(self._fin)
1618 and procutil.isstdout(self._fout)
1625 and procutil.isstdout(self._fout)
1619 )
1626 )
1620 if usereadline:
1627 if usereadline:
1621 try:
1628 try:
1622 # magically add command line editing support, where
1629 # magically add command line editing support, where
1623 # available
1630 # available
1624 import readline
1631 import readline
1625
1632
1626 # force demandimport to really load the module
1633 # force demandimport to really load the module
1627 readline.read_history_file
1634 readline.read_history_file
1628 # windows sometimes raises something other than ImportError
1635 # windows sometimes raises something other than ImportError
1629 except Exception:
1636 except Exception:
1630 usereadline = False
1637 usereadline = False
1631
1638
1632 if self._colormode == b'win32' or not usereadline:
1639 if self._colormode == b'win32' or not usereadline:
1633 if not promptopts:
1640 if not promptopts:
1634 promptopts = {}
1641 promptopts = {}
1635 self._writemsgnobuf(
1642 self._writemsgnobuf(
1636 self._fmsgout, prompt, type=b'prompt', **promptopts
1643 self._fmsgout, prompt, type=b'prompt', **promptopts
1637 )
1644 )
1638 self.flush()
1645 self.flush()
1639 prompt = b' '
1646 prompt = b' '
1640 else:
1647 else:
1641 prompt = self.label(prompt, b'ui.prompt') + b' '
1648 prompt = self.label(prompt, b'ui.prompt') + b' '
1642
1649
1643 # prompt ' ' must exist; otherwise readline may delete entire line
1650 # prompt ' ' must exist; otherwise readline may delete entire line
1644 # - http://bugs.python.org/issue12833
1651 # - http://bugs.python.org/issue12833
1645 with self.timeblockedsection(b'stdio'):
1652 with self.timeblockedsection(b'stdio'):
1646 if usereadline:
1653 if usereadline:
1647 self.flush()
1654 self.flush()
1648 prompt = encoding.strfromlocal(prompt)
1655 prompt = encoding.strfromlocal(prompt)
1649 line = encoding.strtolocal(pycompat.rawinput(prompt))
1656 line = encoding.strtolocal(pycompat.rawinput(prompt))
1650 # When stdin is in binary mode on Windows, it can cause
1657 # When stdin is in binary mode on Windows, it can cause
1651 # raw_input() to emit an extra trailing carriage return
1658 # raw_input() to emit an extra trailing carriage return
1652 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1659 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1653 line = line[:-1]
1660 line = line[:-1]
1654 else:
1661 else:
1655 self._fout.write(pycompat.bytestr(prompt))
1662 self._fout.write(pycompat.bytestr(prompt))
1656 self._fout.flush()
1663 self._fout.flush()
1657 line = self._fin.readline()
1664 line = self._fin.readline()
1658 if not line:
1665 if not line:
1659 raise EOFError
1666 raise EOFError
1660 line = line.rstrip(pycompat.oslinesep)
1667 line = line.rstrip(pycompat.oslinesep)
1661
1668
1662 return line
1669 return line
1663
1670
1664 def prompt(self, msg, default=b"y"):
1671 def prompt(self, msg, default=b"y"):
1665 """Prompt user with msg, read response.
1672 """Prompt user with msg, read response.
1666 If ui is not interactive, the default is returned.
1673 If ui is not interactive, the default is returned.
1667 """
1674 """
1668 return self._prompt(msg, default=default)
1675 return self._prompt(msg, default=default)
1669
1676
1670 def _prompt(self, msg, **opts):
1677 def _prompt(self, msg, **opts):
1671 default = opts['default']
1678 default = opts['default']
1672 if not self.interactive():
1679 if not self.interactive():
1673 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1680 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1674 self._writemsg(
1681 self._writemsg(
1675 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1682 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1676 )
1683 )
1677 return default
1684 return default
1678 try:
1685 try:
1679 r = self._readline(prompt=msg, promptopts=opts)
1686 r = self._readline(prompt=msg, promptopts=opts)
1680 if not r:
1687 if not r:
1681 r = default
1688 r = default
1682 if self.configbool(b'ui', b'promptecho'):
1689 if self.configbool(b'ui', b'promptecho'):
1683 self._writemsg(
1690 self._writemsg(
1684 self._fmsgout, r or b'', b"\n", type=b'promptecho'
1691 self._fmsgout, r or b'', b"\n", type=b'promptecho'
1685 )
1692 )
1686 return r
1693 return r
1687 except EOFError:
1694 except EOFError:
1688 raise error.ResponseExpected()
1695 raise error.ResponseExpected()
1689
1696
1690 @staticmethod
1697 @staticmethod
1691 def extractchoices(prompt):
1698 def extractchoices(prompt):
1692 """Extract prompt message and list of choices from specified prompt.
1699 """Extract prompt message and list of choices from specified prompt.
1693
1700
1694 This returns tuple "(message, choices)", and "choices" is the
1701 This returns tuple "(message, choices)", and "choices" is the
1695 list of tuple "(response character, text without &)".
1702 list of tuple "(response character, text without &)".
1696
1703
1697 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1704 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1698 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1705 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1699 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1706 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1700 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1707 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1701 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1708 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1702 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1709 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1703 """
1710 """
1704
1711
1705 # Sadly, the prompt string may have been built with a filename
1712 # Sadly, the prompt string may have been built with a filename
1706 # containing "$$" so let's try to find the first valid-looking
1713 # containing "$$" so let's try to find the first valid-looking
1707 # prompt to start parsing. Sadly, we also can't rely on
1714 # prompt to start parsing. Sadly, we also can't rely on
1708 # choices containing spaces, ASCII, or basically anything
1715 # choices containing spaces, ASCII, or basically anything
1709 # except an ampersand followed by a character.
1716 # except an ampersand followed by a character.
1710 m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt)
1717 m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt)
1711 msg = m.group(1)
1718 msg = m.group(1)
1712 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1719 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1713
1720
1714 def choicetuple(s):
1721 def choicetuple(s):
1715 ampidx = s.index(b'&')
1722 ampidx = s.index(b'&')
1716 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1723 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1717
1724
1718 return (msg, [choicetuple(s) for s in choices])
1725 return (msg, [choicetuple(s) for s in choices])
1719
1726
1720 def promptchoice(self, prompt, default=0):
1727 def promptchoice(self, prompt, default=0):
1721 """Prompt user with a message, read response, and ensure it matches
1728 """Prompt user with a message, read response, and ensure it matches
1722 one of the provided choices. The prompt is formatted as follows:
1729 one of the provided choices. The prompt is formatted as follows:
1723
1730
1724 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1731 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1725
1732
1726 The index of the choice is returned. Responses are case
1733 The index of the choice is returned. Responses are case
1727 insensitive. If ui is not interactive, the default is
1734 insensitive. If ui is not interactive, the default is
1728 returned.
1735 returned.
1729 """
1736 """
1730
1737
1731 msg, choices = self.extractchoices(prompt)
1738 msg, choices = self.extractchoices(prompt)
1732 resps = [r for r, t in choices]
1739 resps = [r for r, t in choices]
1733 while True:
1740 while True:
1734 r = self._prompt(msg, default=resps[default], choices=choices)
1741 r = self._prompt(msg, default=resps[default], choices=choices)
1735 if r.lower() in resps:
1742 if r.lower() in resps:
1736 return resps.index(r.lower())
1743 return resps.index(r.lower())
1737 # TODO: shouldn't it be a warning?
1744 # TODO: shouldn't it be a warning?
1738 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1745 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1739
1746
1740 def getpass(self, prompt=None, default=None):
1747 def getpass(self, prompt=None, default=None):
1741 if not self.interactive():
1748 if not self.interactive():
1742 return default
1749 return default
1743 try:
1750 try:
1744 self._writemsg(
1751 self._writemsg(
1745 self._fmsgerr,
1752 self._fmsgerr,
1746 prompt or _(b'password: '),
1753 prompt or _(b'password: '),
1747 type=b'prompt',
1754 type=b'prompt',
1748 password=True,
1755 password=True,
1749 )
1756 )
1750 # disable getpass() only if explicitly specified. it's still valid
1757 # disable getpass() only if explicitly specified. it's still valid
1751 # to interact with tty even if fin is not a tty.
1758 # to interact with tty even if fin is not a tty.
1752 with self.timeblockedsection(b'stdio'):
1759 with self.timeblockedsection(b'stdio'):
1753 if self.configbool(b'ui', b'nontty'):
1760 if self.configbool(b'ui', b'nontty'):
1754 l = self._fin.readline()
1761 l = self._fin.readline()
1755 if not l:
1762 if not l:
1756 raise EOFError
1763 raise EOFError
1757 return l.rstrip(b'\n')
1764 return l.rstrip(b'\n')
1758 else:
1765 else:
1759 return encoding.strtolocal(getpass.getpass(''))
1766 return encoding.strtolocal(getpass.getpass(''))
1760 except EOFError:
1767 except EOFError:
1761 raise error.ResponseExpected()
1768 raise error.ResponseExpected()
1762
1769
1763 def status(self, *msg, **opts):
1770 def status(self, *msg, **opts):
1764 """write status message to output (if ui.quiet is False)
1771 """write status message to output (if ui.quiet is False)
1765
1772
1766 This adds an output label of "ui.status".
1773 This adds an output label of "ui.status".
1767 """
1774 """
1768 if not self.quiet:
1775 if not self.quiet:
1769 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1776 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1770
1777
1771 def warn(self, *msg, **opts):
1778 def warn(self, *msg, **opts):
1772 """write warning message to output (stderr)
1779 """write warning message to output (stderr)
1773
1780
1774 This adds an output label of "ui.warning".
1781 This adds an output label of "ui.warning".
1775 """
1782 """
1776 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1783 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1777
1784
1778 def error(self, *msg, **opts):
1785 def error(self, *msg, **opts):
1779 """write error message to output (stderr)
1786 """write error message to output (stderr)
1780
1787
1781 This adds an output label of "ui.error".
1788 This adds an output label of "ui.error".
1782 """
1789 """
1783 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1790 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1784
1791
1785 def note(self, *msg, **opts):
1792 def note(self, *msg, **opts):
1786 """write note to output (if ui.verbose is True)
1793 """write note to output (if ui.verbose is True)
1787
1794
1788 This adds an output label of "ui.note".
1795 This adds an output label of "ui.note".
1789 """
1796 """
1790 if self.verbose:
1797 if self.verbose:
1791 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1798 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1792
1799
1793 def debug(self, *msg, **opts):
1800 def debug(self, *msg, **opts):
1794 """write debug message to output (if ui.debugflag is True)
1801 """write debug message to output (if ui.debugflag is True)
1795
1802
1796 This adds an output label of "ui.debug".
1803 This adds an output label of "ui.debug".
1797 """
1804 """
1798 if self.debugflag:
1805 if self.debugflag:
1799 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1806 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1800 self.log(b'debug', b'%s', b''.join(msg))
1807 self.log(b'debug', b'%s', b''.join(msg))
1801
1808
1802 # Aliases to defeat check-code.
1809 # Aliases to defeat check-code.
1803 statusnoi18n = status
1810 statusnoi18n = status
1804 notenoi18n = note
1811 notenoi18n = note
1805 warnnoi18n = warn
1812 warnnoi18n = warn
1806 writenoi18n = write
1813 writenoi18n = write
1807
1814
1808 def edit(
1815 def edit(
1809 self,
1816 self,
1810 text,
1817 text,
1811 user,
1818 user,
1812 extra=None,
1819 extra=None,
1813 editform=None,
1820 editform=None,
1814 pending=None,
1821 pending=None,
1815 repopath=None,
1822 repopath=None,
1816 action=None,
1823 action=None,
1817 ):
1824 ):
1818 if action is None:
1825 if action is None:
1819 self.develwarn(
1826 self.develwarn(
1820 b'action is None but will soon be a required '
1827 b'action is None but will soon be a required '
1821 b'parameter to ui.edit()'
1828 b'parameter to ui.edit()'
1822 )
1829 )
1823 extra_defaults = {
1830 extra_defaults = {
1824 b'prefix': b'editor',
1831 b'prefix': b'editor',
1825 b'suffix': b'.txt',
1832 b'suffix': b'.txt',
1826 }
1833 }
1827 if extra is not None:
1834 if extra is not None:
1828 if extra.get(b'suffix') is not None:
1835 if extra.get(b'suffix') is not None:
1829 self.develwarn(
1836 self.develwarn(
1830 b'extra.suffix is not None but will soon be '
1837 b'extra.suffix is not None but will soon be '
1831 b'ignored by ui.edit()'
1838 b'ignored by ui.edit()'
1832 )
1839 )
1833 extra_defaults.update(extra)
1840 extra_defaults.update(extra)
1834 extra = extra_defaults
1841 extra = extra_defaults
1835
1842
1836 if action == b'diff':
1843 if action == b'diff':
1837 suffix = b'.diff'
1844 suffix = b'.diff'
1838 elif action:
1845 elif action:
1839 suffix = b'.%s.hg.txt' % action
1846 suffix = b'.%s.hg.txt' % action
1840 else:
1847 else:
1841 suffix = extra[b'suffix']
1848 suffix = extra[b'suffix']
1842
1849
1843 rdir = None
1850 rdir = None
1844 if self.configbool(b'experimental', b'editortmpinhg'):
1851 if self.configbool(b'experimental', b'editortmpinhg'):
1845 rdir = repopath
1852 rdir = repopath
1846 (fd, name) = pycompat.mkstemp(
1853 (fd, name) = pycompat.mkstemp(
1847 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1854 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1848 )
1855 )
1849 try:
1856 try:
1850 with os.fdopen(fd, 'wb') as f:
1857 with os.fdopen(fd, 'wb') as f:
1851 f.write(util.tonativeeol(text))
1858 f.write(util.tonativeeol(text))
1852
1859
1853 environ = {b'HGUSER': user}
1860 environ = {b'HGUSER': user}
1854 if b'transplant_source' in extra:
1861 if b'transplant_source' in extra:
1855 environ.update(
1862 environ.update(
1856 {b'HGREVISION': hex(extra[b'transplant_source'])}
1863 {b'HGREVISION': hex(extra[b'transplant_source'])}
1857 )
1864 )
1858 for label in (b'intermediate-source', b'source', b'rebase_source'):
1865 for label in (b'intermediate-source', b'source', b'rebase_source'):
1859 if label in extra:
1866 if label in extra:
1860 environ.update({b'HGREVISION': extra[label]})
1867 environ.update({b'HGREVISION': extra[label]})
1861 break
1868 break
1862 if editform:
1869 if editform:
1863 environ.update({b'HGEDITFORM': editform})
1870 environ.update({b'HGEDITFORM': editform})
1864 if pending:
1871 if pending:
1865 environ.update({b'HG_PENDING': pending})
1872 environ.update({b'HG_PENDING': pending})
1866
1873
1867 editor = self.geteditor()
1874 editor = self.geteditor()
1868
1875
1869 self.system(
1876 self.system(
1870 b"%s \"%s\"" % (editor, name),
1877 b"%s \"%s\"" % (editor, name),
1871 environ=environ,
1878 environ=environ,
1872 onerr=error.CanceledError,
1879 onerr=error.CanceledError,
1873 errprefix=_(b"edit failed"),
1880 errprefix=_(b"edit failed"),
1874 blockedtag=b'editor',
1881 blockedtag=b'editor',
1875 )
1882 )
1876
1883
1877 with open(name, 'rb') as f:
1884 with open(name, 'rb') as f:
1878 t = util.fromnativeeol(f.read())
1885 t = util.fromnativeeol(f.read())
1879 finally:
1886 finally:
1880 os.unlink(name)
1887 os.unlink(name)
1881
1888
1882 return t
1889 return t
1883
1890
1884 def system(
1891 def system(
1885 self,
1892 self,
1886 cmd,
1893 cmd,
1887 environ=None,
1894 environ=None,
1888 cwd=None,
1895 cwd=None,
1889 onerr=None,
1896 onerr=None,
1890 errprefix=None,
1897 errprefix=None,
1891 blockedtag=None,
1898 blockedtag=None,
1892 ):
1899 ):
1893 """execute shell command with appropriate output stream. command
1900 """execute shell command with appropriate output stream. command
1894 output will be redirected if fout is not stdout.
1901 output will be redirected if fout is not stdout.
1895
1902
1896 if command fails and onerr is None, return status, else raise onerr
1903 if command fails and onerr is None, return status, else raise onerr
1897 object as exception.
1904 object as exception.
1898 """
1905 """
1899 if blockedtag is None:
1906 if blockedtag is None:
1900 # Long cmds tend to be because of an absolute path on cmd. Keep
1907 # Long cmds tend to be because of an absolute path on cmd. Keep
1901 # the tail end instead
1908 # the tail end instead
1902 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1909 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1903 blockedtag = b'unknown_system_' + cmdsuffix
1910 blockedtag = b'unknown_system_' + cmdsuffix
1904 out = self._fout
1911 out = self._fout
1905 if any(s[1] for s in self._bufferstates):
1912 if any(s[1] for s in self._bufferstates):
1906 out = self
1913 out = self
1907 with self.timeblockedsection(blockedtag):
1914 with self.timeblockedsection(blockedtag):
1908 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1915 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1909 if rc and onerr:
1916 if rc and onerr:
1910 errmsg = b'%s %s' % (
1917 errmsg = b'%s %s' % (
1911 procutil.shellsplit(cmd)[0],
1918 procutil.shellsplit(cmd)[0],
1912 procutil.explainexit(rc),
1919 procutil.explainexit(rc),
1913 )
1920 )
1914 if errprefix:
1921 if errprefix:
1915 errmsg = b'%s: %s' % (errprefix, errmsg)
1922 errmsg = b'%s: %s' % (errprefix, errmsg)
1916 raise onerr(errmsg)
1923 raise onerr(errmsg)
1917 return rc
1924 return rc
1918
1925
1919 def _runsystem(self, cmd, environ, cwd, out):
1926 def _runsystem(self, cmd, environ, cwd, out):
1920 """actually execute the given shell command (can be overridden by
1927 """actually execute the given shell command (can be overridden by
1921 extensions like chg)"""
1928 extensions like chg)"""
1922 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1929 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1923
1930
1924 def traceback(self, exc=None, force=False):
1931 def traceback(self, exc=None, force=False):
1925 """print exception traceback if traceback printing enabled or forced.
1932 """print exception traceback if traceback printing enabled or forced.
1926 only to call in exception handler. returns true if traceback
1933 only to call in exception handler. returns true if traceback
1927 printed."""
1934 printed."""
1928 if self.tracebackflag or force:
1935 if self.tracebackflag or force:
1929 if exc is None:
1936 if exc is None:
1930 exc = sys.exc_info()
1937 exc = sys.exc_info()
1931 cause = getattr(exc[1], 'cause', None)
1938 cause = getattr(exc[1], 'cause', None)
1932
1939
1933 if cause is not None:
1940 if cause is not None:
1934 causetb = traceback.format_tb(cause[2])
1941 causetb = traceback.format_tb(cause[2])
1935 exctb = traceback.format_tb(exc[2])
1942 exctb = traceback.format_tb(exc[2])
1936 exconly = traceback.format_exception_only(cause[0], cause[1])
1943 exconly = traceback.format_exception_only(cause[0], cause[1])
1937
1944
1938 # exclude frame where 'exc' was chained and rethrown from exctb
1945 # exclude frame where 'exc' was chained and rethrown from exctb
1939 self.write_err(
1946 self.write_err(
1940 b'Traceback (most recent call last):\n',
1947 b'Traceback (most recent call last):\n',
1941 encoding.strtolocal(''.join(exctb[:-1])),
1948 encoding.strtolocal(''.join(exctb[:-1])),
1942 encoding.strtolocal(''.join(causetb)),
1949 encoding.strtolocal(''.join(causetb)),
1943 encoding.strtolocal(''.join(exconly)),
1950 encoding.strtolocal(''.join(exconly)),
1944 )
1951 )
1945 else:
1952 else:
1946 output = traceback.format_exception(exc[0], exc[1], exc[2])
1953 output = traceback.format_exception(exc[0], exc[1], exc[2])
1947 self.write_err(encoding.strtolocal(''.join(output)))
1954 self.write_err(encoding.strtolocal(''.join(output)))
1948 return self.tracebackflag or force
1955 return self.tracebackflag or force
1949
1956
1950 def geteditor(self):
1957 def geteditor(self):
1951 '''return editor to use'''
1958 '''return editor to use'''
1952 if pycompat.sysplatform == b'plan9':
1959 if pycompat.sysplatform == b'plan9':
1953 # vi is the MIPS instruction simulator on Plan 9. We
1960 # vi is the MIPS instruction simulator on Plan 9. We
1954 # instead default to E to plumb commit messages to
1961 # instead default to E to plumb commit messages to
1955 # avoid confusion.
1962 # avoid confusion.
1956 editor = b'E'
1963 editor = b'E'
1957 elif pycompat.isdarwin:
1964 elif pycompat.isdarwin:
1958 # vi on darwin is POSIX compatible to a fault, and that includes
1965 # vi on darwin is POSIX compatible to a fault, and that includes
1959 # exiting non-zero if you make any mistake when running an ex
1966 # exiting non-zero if you make any mistake when running an ex
1960 # command. Proof: `vi -c ':unknown' -c ':qa'; echo $?` produces 1,
1967 # command. Proof: `vi -c ':unknown' -c ':qa'; echo $?` produces 1,
1961 # while s/vi/vim/ doesn't.
1968 # while s/vi/vim/ doesn't.
1962 editor = b'vim'
1969 editor = b'vim'
1963 else:
1970 else:
1964 editor = b'vi'
1971 editor = b'vi'
1965 return encoding.environ.get(b"HGEDITOR") or self.config(
1972 return encoding.environ.get(b"HGEDITOR") or self.config(
1966 b"ui", b"editor", editor
1973 b"ui", b"editor", editor
1967 )
1974 )
1968
1975
1969 @util.propertycache
1976 @util.propertycache
1970 def _progbar(self):
1977 def _progbar(self):
1971 """setup the progbar singleton to the ui object"""
1978 """setup the progbar singleton to the ui object"""
1972 if (
1979 if (
1973 self.quiet
1980 self.quiet
1974 or self.debugflag
1981 or self.debugflag
1975 or self.configbool(b'progress', b'disable')
1982 or self.configbool(b'progress', b'disable')
1976 or not progress.shouldprint(self)
1983 or not progress.shouldprint(self)
1977 ):
1984 ):
1978 return None
1985 return None
1979 return getprogbar(self)
1986 return getprogbar(self)
1980
1987
1981 def _progclear(self):
1988 def _progclear(self):
1982 """clear progress bar output if any. use it before any output"""
1989 """clear progress bar output if any. use it before any output"""
1983 if not haveprogbar(): # nothing loaded yet
1990 if not haveprogbar(): # nothing loaded yet
1984 return
1991 return
1985 if self._progbar is not None and self._progbar.printed:
1992 if self._progbar is not None and self._progbar.printed:
1986 self._progbar.clear()
1993 self._progbar.clear()
1987
1994
1988 def makeprogress(self, topic, unit=b"", total=None):
1995 def makeprogress(self, topic, unit=b"", total=None):
1989 """Create a progress helper for the specified topic"""
1996 """Create a progress helper for the specified topic"""
1990 if getattr(self._fmsgerr, 'structured', False):
1997 if getattr(self._fmsgerr, 'structured', False):
1991 # channel for machine-readable output with metadata, just send
1998 # channel for machine-readable output with metadata, just send
1992 # raw information
1999 # raw information
1993 # TODO: consider porting some useful information (e.g. estimated
2000 # TODO: consider porting some useful information (e.g. estimated
1994 # time) from progbar. we might want to support update delay to
2001 # time) from progbar. we might want to support update delay to
1995 # reduce the cost of transferring progress messages.
2002 # reduce the cost of transferring progress messages.
1996 def updatebar(topic, pos, item, unit, total):
2003 def updatebar(topic, pos, item, unit, total):
1997 self._fmsgerr.write(
2004 self._fmsgerr.write(
1998 None,
2005 None,
1999 type=b'progress',
2006 type=b'progress',
2000 topic=topic,
2007 topic=topic,
2001 pos=pos,
2008 pos=pos,
2002 item=item,
2009 item=item,
2003 unit=unit,
2010 unit=unit,
2004 total=total,
2011 total=total,
2005 )
2012 )
2006
2013
2007 elif self._progbar is not None:
2014 elif self._progbar is not None:
2008 updatebar = self._progbar.progress
2015 updatebar = self._progbar.progress
2009 else:
2016 else:
2010
2017
2011 def updatebar(topic, pos, item, unit, total):
2018 def updatebar(topic, pos, item, unit, total):
2012 pass
2019 pass
2013
2020
2014 return scmutil.progress(self, updatebar, topic, unit, total)
2021 return scmutil.progress(self, updatebar, topic, unit, total)
2015
2022
2016 def getlogger(self, name):
2023 def getlogger(self, name):
2017 """Returns a logger of the given name; or None if not registered"""
2024 """Returns a logger of the given name; or None if not registered"""
2018 return self._loggers.get(name)
2025 return self._loggers.get(name)
2019
2026
2020 def setlogger(self, name, logger):
2027 def setlogger(self, name, logger):
2021 """Install logger which can be identified later by the given name
2028 """Install logger which can be identified later by the given name
2022
2029
2023 More than one loggers can be registered. Use extension or module
2030 More than one loggers can be registered. Use extension or module
2024 name to uniquely identify the logger instance.
2031 name to uniquely identify the logger instance.
2025 """
2032 """
2026 self._loggers[name] = logger
2033 self._loggers[name] = logger
2027
2034
2028 def log(self, event, msgfmt, *msgargs, **opts):
2035 def log(self, event, msgfmt, *msgargs, **opts):
2029 """hook for logging facility extensions
2036 """hook for logging facility extensions
2030
2037
2031 event should be a readily-identifiable subsystem, which will
2038 event should be a readily-identifiable subsystem, which will
2032 allow filtering.
2039 allow filtering.
2033
2040
2034 msgfmt should be a newline-terminated format string to log, and
2041 msgfmt should be a newline-terminated format string to log, and
2035 *msgargs are %-formatted into it.
2042 *msgargs are %-formatted into it.
2036
2043
2037 **opts currently has no defined meanings.
2044 **opts currently has no defined meanings.
2038 """
2045 """
2039 if not self._loggers:
2046 if not self._loggers:
2040 return
2047 return
2041 activeloggers = [
2048 activeloggers = [
2042 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
2049 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
2043 ]
2050 ]
2044 if not activeloggers:
2051 if not activeloggers:
2045 return
2052 return
2046 msg = msgfmt % msgargs
2053 msg = msgfmt % msgargs
2047 opts = pycompat.byteskwargs(opts)
2054 opts = pycompat.byteskwargs(opts)
2048 # guard against recursion from e.g. ui.debug()
2055 # guard against recursion from e.g. ui.debug()
2049 registeredloggers = self._loggers
2056 registeredloggers = self._loggers
2050 self._loggers = {}
2057 self._loggers = {}
2051 try:
2058 try:
2052 for logger in activeloggers:
2059 for logger in activeloggers:
2053 logger.log(self, event, msg, opts)
2060 logger.log(self, event, msg, opts)
2054 finally:
2061 finally:
2055 self._loggers = registeredloggers
2062 self._loggers = registeredloggers
2056
2063
2057 def label(self, msg, label):
2064 def label(self, msg, label):
2058 """style msg based on supplied label
2065 """style msg based on supplied label
2059
2066
2060 If some color mode is enabled, this will add the necessary control
2067 If some color mode is enabled, this will add the necessary control
2061 characters to apply such color. In addition, 'debug' color mode adds
2068 characters to apply such color. In addition, 'debug' color mode adds
2062 markup showing which label affects a piece of text.
2069 markup showing which label affects a piece of text.
2063
2070
2064 ui.write(s, 'label') is equivalent to
2071 ui.write(s, 'label') is equivalent to
2065 ui.write(ui.label(s, 'label')).
2072 ui.write(ui.label(s, 'label')).
2066 """
2073 """
2067 if self._colormode is not None:
2074 if self._colormode is not None:
2068 return color.colorlabel(self, msg, label)
2075 return color.colorlabel(self, msg, label)
2069 return msg
2076 return msg
2070
2077
2071 def develwarn(self, msg, stacklevel=1, config=None):
2078 def develwarn(self, msg, stacklevel=1, config=None):
2072 """issue a developer warning message
2079 """issue a developer warning message
2073
2080
2074 Use 'stacklevel' to report the offender some layers further up in the
2081 Use 'stacklevel' to report the offender some layers further up in the
2075 stack.
2082 stack.
2076 """
2083 """
2077 if not self.configbool(b'devel', b'all-warnings'):
2084 if not self.configbool(b'devel', b'all-warnings'):
2078 if config is None or not self.configbool(b'devel', config):
2085 if config is None or not self.configbool(b'devel', config):
2079 return
2086 return
2080 msg = b'devel-warn: ' + msg
2087 msg = b'devel-warn: ' + msg
2081 stacklevel += 1 # get in develwarn
2088 stacklevel += 1 # get in develwarn
2082 if self.tracebackflag:
2089 if self.tracebackflag:
2083 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2090 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2084 self.log(
2091 self.log(
2085 b'develwarn',
2092 b'develwarn',
2086 b'%s at:\n%s'
2093 b'%s at:\n%s'
2087 % (msg, b''.join(util.getstackframes(stacklevel))),
2094 % (msg, b''.join(util.getstackframes(stacklevel))),
2088 )
2095 )
2089 else:
2096 else:
2090 curframe = inspect.currentframe()
2097 curframe = inspect.currentframe()
2091 calframe = inspect.getouterframes(curframe, 2)
2098 calframe = inspect.getouterframes(curframe, 2)
2092 fname, lineno, fmsg = calframe[stacklevel][1:4]
2099 fname, lineno, fmsg = calframe[stacklevel][1:4]
2093 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2100 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2094 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2101 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2095 self.log(
2102 self.log(
2096 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2103 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2097 )
2104 )
2098
2105
2099 # avoid cycles
2106 # avoid cycles
2100 del curframe
2107 del curframe
2101 del calframe
2108 del calframe
2102
2109
2103 def deprecwarn(self, msg, version, stacklevel=2):
2110 def deprecwarn(self, msg, version, stacklevel=2):
2104 """issue a deprecation warning
2111 """issue a deprecation warning
2105
2112
2106 - msg: message explaining what is deprecated and how to upgrade,
2113 - msg: message explaining what is deprecated and how to upgrade,
2107 - version: last version where the API will be supported,
2114 - version: last version where the API will be supported,
2108 """
2115 """
2109 if not (
2116 if not (
2110 self.configbool(b'devel', b'all-warnings')
2117 self.configbool(b'devel', b'all-warnings')
2111 or self.configbool(b'devel', b'deprec-warn')
2118 or self.configbool(b'devel', b'deprec-warn')
2112 ):
2119 ):
2113 return
2120 return
2114 msg += (
2121 msg += (
2115 b"\n(compatibility will be dropped after Mercurial-%s,"
2122 b"\n(compatibility will be dropped after Mercurial-%s,"
2116 b" update your code.)"
2123 b" update your code.)"
2117 ) % version
2124 ) % version
2118 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2125 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2119
2126
2120 def exportableenviron(self):
2127 def exportableenviron(self):
2121 """The environment variables that are safe to export, e.g. through
2128 """The environment variables that are safe to export, e.g. through
2122 hgweb.
2129 hgweb.
2123 """
2130 """
2124 return self._exportableenviron
2131 return self._exportableenviron
2125
2132
2126 @contextlib.contextmanager
2133 @contextlib.contextmanager
2127 def configoverride(self, overrides, source=b""):
2134 def configoverride(self, overrides, source=b""):
2128 """Context manager for temporary config overrides
2135 """Context manager for temporary config overrides
2129 `overrides` must be a dict of the following structure:
2136 `overrides` must be a dict of the following structure:
2130 {(section, name) : value}"""
2137 {(section, name) : value}"""
2131 backups = {}
2138 backups = {}
2132 try:
2139 try:
2133 for (section, name), value in overrides.items():
2140 for (section, name), value in overrides.items():
2134 backups[(section, name)] = self.backupconfig(section, name)
2141 backups[(section, name)] = self.backupconfig(section, name)
2135 self.setconfig(section, name, value, source)
2142 self.setconfig(section, name, value, source)
2136 yield
2143 yield
2137 finally:
2144 finally:
2138 for __, backup in backups.items():
2145 for __, backup in backups.items():
2139 self.restoreconfig(backup)
2146 self.restoreconfig(backup)
2140 # just restoring ui.quiet config to the previous value is not enough
2147 # just restoring ui.quiet config to the previous value is not enough
2141 # as it does not update ui.quiet class member
2148 # as it does not update ui.quiet class member
2142 if (b'ui', b'quiet') in overrides:
2149 if (b'ui', b'quiet') in overrides:
2143 self.fixconfig(section=b'ui')
2150 self.fixconfig(section=b'ui')
2144
2151
2145 def estimatememory(self):
2152 def estimatememory(self):
2146 """Provide an estimate for the available system memory in Bytes.
2153 """Provide an estimate for the available system memory in Bytes.
2147
2154
2148 This can be overriden via ui.available-memory. It returns None, if
2155 This can be overriden via ui.available-memory. It returns None, if
2149 no estimate can be computed.
2156 no estimate can be computed.
2150 """
2157 """
2151 value = self.config(b'ui', b'available-memory')
2158 value = self.config(b'ui', b'available-memory')
2152 if value is not None:
2159 if value is not None:
2153 try:
2160 try:
2154 return util.sizetoint(value)
2161 return util.sizetoint(value)
2155 except error.ParseError:
2162 except error.ParseError:
2156 raise error.ConfigError(
2163 raise error.ConfigError(
2157 _(b"ui.available-memory value is invalid ('%s')") % value
2164 _(b"ui.available-memory value is invalid ('%s')") % value
2158 )
2165 )
2159 return util._estimatememory()
2166 return util._estimatememory()
2160
2167
2161
2168
2162 class paths(dict):
2169 class paths(dict):
2163 """Represents a collection of paths and their configs.
2170 """Represents a collection of paths and their configs.
2164
2171
2165 Data is initially derived from ui instances and the config files they have
2172 Data is initially derived from ui instances and the config files they have
2166 loaded.
2173 loaded.
2167 """
2174 """
2168
2175
2169 def __init__(self, ui):
2176 def __init__(self, ui):
2170 dict.__init__(self)
2177 dict.__init__(self)
2171
2178
2172 for name, loc in ui.configitems(b'paths', ignoresub=True):
2179 for name, loc in ui.configitems(b'paths', ignoresub=True):
2173 # No location is the same as not existing.
2180 # No location is the same as not existing.
2174 if not loc:
2181 if not loc:
2175 continue
2182 continue
2176 loc, sub = ui.configsuboptions(b'paths', name)
2183 loc, sub = ui.configsuboptions(b'paths', name)
2177 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2184 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2178
2185
2179 def getpath(self, name, default=None):
2186 def getpath(self, name, default=None):
2180 """Return a ``path`` from a string, falling back to default.
2187 """Return a ``path`` from a string, falling back to default.
2181
2188
2182 ``name`` can be a named path or locations. Locations are filesystem
2189 ``name`` can be a named path or locations. Locations are filesystem
2183 paths or URIs.
2190 paths or URIs.
2184
2191
2185 Returns None if ``name`` is not a registered path, a URI, or a local
2192 Returns None if ``name`` is not a registered path, a URI, or a local
2186 path to a repo.
2193 path to a repo.
2187 """
2194 """
2188 # Only fall back to default if no path was requested.
2195 # Only fall back to default if no path was requested.
2189 if name is None:
2196 if name is None:
2190 if not default:
2197 if not default:
2191 default = ()
2198 default = ()
2192 elif not isinstance(default, (tuple, list)):
2199 elif not isinstance(default, (tuple, list)):
2193 default = (default,)
2200 default = (default,)
2194 for k in default:
2201 for k in default:
2195 try:
2202 try:
2196 return self[k]
2203 return self[k]
2197 except KeyError:
2204 except KeyError:
2198 continue
2205 continue
2199 return None
2206 return None
2200
2207
2201 # Most likely empty string.
2208 # Most likely empty string.
2202 # This may need to raise in the future.
2209 # This may need to raise in the future.
2203 if not name:
2210 if not name:
2204 return None
2211 return None
2205
2212
2206 try:
2213 try:
2207 return self[name]
2214 return self[name]
2208 except KeyError:
2215 except KeyError:
2209 # Try to resolve as a local path or URI.
2216 # Try to resolve as a local path or URI.
2210 try:
2217 try:
2211 # We don't pass sub-options in, so no need to pass ui instance.
2218 # We don't pass sub-options in, so no need to pass ui instance.
2212 return path(None, None, rawloc=name)
2219 return path(None, None, rawloc=name)
2213 except ValueError:
2220 except ValueError:
2214 raise error.RepoError(_(b'repository %s does not exist') % name)
2221 raise error.RepoError(_(b'repository %s does not exist') % name)
2215
2222
2216
2223
2217 _pathsuboptions = {}
2224 _pathsuboptions = {}
2218
2225
2219
2226
2220 def pathsuboption(option, attr):
2227 def pathsuboption(option, attr):
2221 """Decorator used to declare a path sub-option.
2228 """Decorator used to declare a path sub-option.
2222
2229
2223 Arguments are the sub-option name and the attribute it should set on
2230 Arguments are the sub-option name and the attribute it should set on
2224 ``path`` instances.
2231 ``path`` instances.
2225
2232
2226 The decorated function will receive as arguments a ``ui`` instance,
2233 The decorated function will receive as arguments a ``ui`` instance,
2227 ``path`` instance, and the string value of this option from the config.
2234 ``path`` instance, and the string value of this option from the config.
2228 The function should return the value that will be set on the ``path``
2235 The function should return the value that will be set on the ``path``
2229 instance.
2236 instance.
2230
2237
2231 This decorator can be used to perform additional verification of
2238 This decorator can be used to perform additional verification of
2232 sub-options and to change the type of sub-options.
2239 sub-options and to change the type of sub-options.
2233 """
2240 """
2234
2241
2235 def register(func):
2242 def register(func):
2236 _pathsuboptions[option] = (attr, func)
2243 _pathsuboptions[option] = (attr, func)
2237 return func
2244 return func
2238
2245
2239 return register
2246 return register
2240
2247
2241
2248
2242 @pathsuboption(b'pushurl', b'pushloc')
2249 @pathsuboption(b'pushurl', b'pushloc')
2243 def pushurlpathoption(ui, path, value):
2250 def pushurlpathoption(ui, path, value):
2244 u = util.url(value)
2251 u = util.url(value)
2245 # Actually require a URL.
2252 # Actually require a URL.
2246 if not u.scheme:
2253 if not u.scheme:
2247 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2254 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2248 return None
2255 return None
2249
2256
2250 # Don't support the #foo syntax in the push URL to declare branch to
2257 # Don't support the #foo syntax in the push URL to declare branch to
2251 # push.
2258 # push.
2252 if u.fragment:
2259 if u.fragment:
2253 ui.warn(
2260 ui.warn(
2254 _(
2261 _(
2255 b'("#fragment" in paths.%s:pushurl not supported; '
2262 b'("#fragment" in paths.%s:pushurl not supported; '
2256 b'ignoring)\n'
2263 b'ignoring)\n'
2257 )
2264 )
2258 % path.name
2265 % path.name
2259 )
2266 )
2260 u.fragment = None
2267 u.fragment = None
2261
2268
2262 return bytes(u)
2269 return bytes(u)
2263
2270
2264
2271
2265 @pathsuboption(b'pushrev', b'pushrev')
2272 @pathsuboption(b'pushrev', b'pushrev')
2266 def pushrevpathoption(ui, path, value):
2273 def pushrevpathoption(ui, path, value):
2267 return value
2274 return value
2268
2275
2269
2276
2270 class path(object):
2277 class path(object):
2271 """Represents an individual path and its configuration."""
2278 """Represents an individual path and its configuration."""
2272
2279
2273 def __init__(self, ui, name, rawloc=None, suboptions=None):
2280 def __init__(self, ui, name, rawloc=None, suboptions=None):
2274 """Construct a path from its config options.
2281 """Construct a path from its config options.
2275
2282
2276 ``ui`` is the ``ui`` instance the path is coming from.
2283 ``ui`` is the ``ui`` instance the path is coming from.
2277 ``name`` is the symbolic name of the path.
2284 ``name`` is the symbolic name of the path.
2278 ``rawloc`` is the raw location, as defined in the config.
2285 ``rawloc`` is the raw location, as defined in the config.
2279 ``pushloc`` is the raw locations pushes should be made to.
2286 ``pushloc`` is the raw locations pushes should be made to.
2280
2287
2281 If ``name`` is not defined, we require that the location be a) a local
2288 If ``name`` is not defined, we require that the location be a) a local
2282 filesystem path with a .hg directory or b) a URL. If not,
2289 filesystem path with a .hg directory or b) a URL. If not,
2283 ``ValueError`` is raised.
2290 ``ValueError`` is raised.
2284 """
2291 """
2285 if not rawloc:
2292 if not rawloc:
2286 raise ValueError(b'rawloc must be defined')
2293 raise ValueError(b'rawloc must be defined')
2287
2294
2288 # Locations may define branches via syntax <base>#<branch>.
2295 # Locations may define branches via syntax <base>#<branch>.
2289 u = util.url(rawloc)
2296 u = util.url(rawloc)
2290 branch = None
2297 branch = None
2291 if u.fragment:
2298 if u.fragment:
2292 branch = u.fragment
2299 branch = u.fragment
2293 u.fragment = None
2300 u.fragment = None
2294
2301
2295 self.url = u
2302 self.url = u
2296 self.branch = branch
2303 self.branch = branch
2297
2304
2298 self.name = name
2305 self.name = name
2299 self.rawloc = rawloc
2306 self.rawloc = rawloc
2300 self.loc = b'%s' % u
2307 self.loc = b'%s' % u
2301
2308
2302 # When given a raw location but not a symbolic name, validate the
2309 # When given a raw location but not a symbolic name, validate the
2303 # location is valid.
2310 # location is valid.
2304 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2311 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2305 raise ValueError(
2312 raise ValueError(
2306 b'location is not a URL or path to a local '
2313 b'location is not a URL or path to a local '
2307 b'repo: %s' % rawloc
2314 b'repo: %s' % rawloc
2308 )
2315 )
2309
2316
2310 suboptions = suboptions or {}
2317 suboptions = suboptions or {}
2311
2318
2312 # Now process the sub-options. If a sub-option is registered, its
2319 # Now process the sub-options. If a sub-option is registered, its
2313 # attribute will always be present. The value will be None if there
2320 # attribute will always be present. The value will be None if there
2314 # was no valid sub-option.
2321 # was no valid sub-option.
2315 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2322 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2316 if suboption not in suboptions:
2323 if suboption not in suboptions:
2317 setattr(self, attr, None)
2324 setattr(self, attr, None)
2318 continue
2325 continue
2319
2326
2320 value = func(ui, self, suboptions[suboption])
2327 value = func(ui, self, suboptions[suboption])
2321 setattr(self, attr, value)
2328 setattr(self, attr, value)
2322
2329
2323 def _isvalidlocalpath(self, path):
2330 def _isvalidlocalpath(self, path):
2324 """Returns True if the given path is a potentially valid repository.
2331 """Returns True if the given path is a potentially valid repository.
2325 This is its own function so that extensions can change the definition of
2332 This is its own function so that extensions can change the definition of
2326 'valid' in this case (like when pulling from a git repo into a hg
2333 'valid' in this case (like when pulling from a git repo into a hg
2327 one)."""
2334 one)."""
2328 try:
2335 try:
2329 return os.path.isdir(os.path.join(path, b'.hg'))
2336 return os.path.isdir(os.path.join(path, b'.hg'))
2330 # Python 2 may return TypeError. Python 3, ValueError.
2337 # Python 2 may return TypeError. Python 3, ValueError.
2331 except (TypeError, ValueError):
2338 except (TypeError, ValueError):
2332 return False
2339 return False
2333
2340
2334 @property
2341 @property
2335 def suboptions(self):
2342 def suboptions(self):
2336 """Return sub-options and their values for this path.
2343 """Return sub-options and their values for this path.
2337
2344
2338 This is intended to be used for presentation purposes.
2345 This is intended to be used for presentation purposes.
2339 """
2346 """
2340 d = {}
2347 d = {}
2341 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2348 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2342 value = getattr(self, attr)
2349 value = getattr(self, attr)
2343 if value is not None:
2350 if value is not None:
2344 d[subopt] = value
2351 d[subopt] = value
2345 return d
2352 return d
2346
2353
2347
2354
2348 # we instantiate one globally shared progress bar to avoid
2355 # we instantiate one globally shared progress bar to avoid
2349 # competing progress bars when multiple UI objects get created
2356 # competing progress bars when multiple UI objects get created
2350 _progresssingleton = None
2357 _progresssingleton = None
2351
2358
2352
2359
2353 def getprogbar(ui):
2360 def getprogbar(ui):
2354 global _progresssingleton
2361 global _progresssingleton
2355 if _progresssingleton is None:
2362 if _progresssingleton is None:
2356 # passing 'ui' object to the singleton is fishy,
2363 # passing 'ui' object to the singleton is fishy,
2357 # this is how the extension used to work but feel free to rework it.
2364 # this is how the extension used to work but feel free to rework it.
2358 _progresssingleton = progress.progbar(ui)
2365 _progresssingleton = progress.progbar(ui)
2359 return _progresssingleton
2366 return _progresssingleton
2360
2367
2361
2368
2362 def haveprogbar():
2369 def haveprogbar():
2363 return _progresssingleton is not None
2370 return _progresssingleton is not None
2364
2371
2365
2372
2366 def _selectmsgdests(ui):
2373 def _selectmsgdests(ui):
2367 name = ui.config(b'ui', b'message-output')
2374 name = ui.config(b'ui', b'message-output')
2368 if name == b'channel':
2375 if name == b'channel':
2369 if ui.fmsg:
2376 if ui.fmsg:
2370 return ui.fmsg, ui.fmsg
2377 return ui.fmsg, ui.fmsg
2371 else:
2378 else:
2372 # fall back to ferr if channel isn't ready so that status/error
2379 # fall back to ferr if channel isn't ready so that status/error
2373 # messages can be printed
2380 # messages can be printed
2374 return ui.ferr, ui.ferr
2381 return ui.ferr, ui.ferr
2375 if name == b'stdio':
2382 if name == b'stdio':
2376 return ui.fout, ui.ferr
2383 return ui.fout, ui.ferr
2377 if name == b'stderr':
2384 if name == b'stderr':
2378 return ui.ferr, ui.ferr
2385 return ui.ferr, ui.ferr
2379 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2386 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2380
2387
2381
2388
2382 def _writemsgwith(write, dest, *args, **opts):
2389 def _writemsgwith(write, dest, *args, **opts):
2383 """Write ui message with the given ui._write*() function
2390 """Write ui message with the given ui._write*() function
2384
2391
2385 The specified message type is translated to 'ui.<type>' label if the dest
2392 The specified message type is translated to 'ui.<type>' label if the dest
2386 isn't a structured channel, so that the message will be colorized.
2393 isn't a structured channel, so that the message will be colorized.
2387 """
2394 """
2388 # TODO: maybe change 'type' to a mandatory option
2395 # TODO: maybe change 'type' to a mandatory option
2389 if 'type' in opts and not getattr(dest, 'structured', False):
2396 if 'type' in opts and not getattr(dest, 'structured', False):
2390 opts['label'] = opts.get('label', b'') + b' ui.%s' % opts.pop('type')
2397 opts['label'] = opts.get('label', b'') + b' ui.%s' % opts.pop('type')
2391 write(dest, *args, **opts)
2398 write(dest, *args, **opts)
General Comments 0
You need to be logged in to leave comments. Login now