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