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