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