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