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