##// END OF EJS Templates
ui: option to preserve the progress bar...
Joerg Sonnenberger -
r43559:82879e06 default
parent child Browse files
Show More
@@ -1,2300 +1,2309 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import getpass
13 import getpass
14 import inspect
14 import inspect
15 import os
15 import os
16 import re
16 import re
17 import signal
17 import signal
18 import socket
18 import socket
19 import subprocess
19 import subprocess
20 import sys
20 import sys
21 import traceback
21 import traceback
22
22
23 from .i18n import _
23 from .i18n import _
24 from .node import hex
24 from .node import hex
25 from .pycompat import (
25 from .pycompat import (
26 getattr,
26 getattr,
27 open,
27 open,
28 setattr,
28 setattr,
29 )
29 )
30
30
31 from . import (
31 from . import (
32 color,
32 color,
33 config,
33 config,
34 configitems,
34 configitems,
35 encoding,
35 encoding,
36 error,
36 error,
37 formatter,
37 formatter,
38 loggingutil,
38 loggingutil,
39 progress,
39 progress,
40 pycompat,
40 pycompat,
41 rcutil,
41 rcutil,
42 scmutil,
42 scmutil,
43 util,
43 util,
44 )
44 )
45 from .utils import (
45 from .utils import (
46 dateutil,
46 dateutil,
47 procutil,
47 procutil,
48 stringutil,
48 stringutil,
49 )
49 )
50
50
51 urlreq = util.urlreq
51 urlreq = util.urlreq
52
52
53 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
53 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
54 _keepalnum = b''.join(
54 _keepalnum = b''.join(
55 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
55 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
56 )
56 )
57
57
58 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
58 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
59 tweakrc = b"""
59 tweakrc = b"""
60 [ui]
60 [ui]
61 # The rollback command is dangerous. As a rule, don't use it.
61 # The rollback command is dangerous. As a rule, don't use it.
62 rollback = False
62 rollback = False
63 # Make `hg status` report copy information
63 # Make `hg status` report copy information
64 statuscopies = yes
64 statuscopies = yes
65 # Prefer curses UIs when available. Revert to plain-text with `text`.
65 # Prefer curses UIs when available. Revert to plain-text with `text`.
66 interface = curses
66 interface = curses
67 # Make compatible commands emit cwd-relative paths by default.
67 # Make compatible commands emit cwd-relative paths by default.
68 relative-paths = yes
68 relative-paths = yes
69
69
70 [commands]
70 [commands]
71 # Grep working directory by default.
71 # Grep working directory by default.
72 grep.all-files = True
72 grep.all-files = True
73 # Refuse to perform an `hg update` that would cause a file content merge
73 # Refuse to perform an `hg update` that would cause a file content merge
74 update.check = noconflict
74 update.check = noconflict
75 # Show conflicts information in `hg status`
75 # Show conflicts information in `hg status`
76 status.verbose = True
76 status.verbose = True
77 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
77 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
78 resolve.explicit-re-merge = True
78 resolve.explicit-re-merge = True
79
79
80 [diff]
80 [diff]
81 git = 1
81 git = 1
82 showfunc = 1
82 showfunc = 1
83 word-diff = 1
83 word-diff = 1
84 """
84 """
85
85
86 samplehgrcs = {
86 samplehgrcs = {
87 b'user': b"""# example user config (see 'hg help config' for more info)
87 b'user': b"""# example user config (see 'hg help config' for more info)
88 [ui]
88 [ui]
89 # name and email, e.g.
89 # name and email, e.g.
90 # username = Jane Doe <jdoe@example.com>
90 # username = Jane Doe <jdoe@example.com>
91 username =
91 username =
92
92
93 # We recommend enabling tweakdefaults to get slight improvements to
93 # We recommend enabling tweakdefaults to get slight improvements to
94 # the UI over time. Make sure to set HGPLAIN in the environment when
94 # the UI over time. Make sure to set HGPLAIN in the environment when
95 # writing scripts!
95 # writing scripts!
96 # tweakdefaults = True
96 # tweakdefaults = True
97
97
98 # uncomment to disable color in command output
98 # uncomment to disable color in command output
99 # (see 'hg help color' for details)
99 # (see 'hg help color' for details)
100 # color = never
100 # color = never
101
101
102 # uncomment to disable command output pagination
102 # uncomment to disable command output pagination
103 # (see 'hg help pager' for details)
103 # (see 'hg help pager' for details)
104 # paginate = never
104 # paginate = never
105
105
106 [extensions]
106 [extensions]
107 # uncomment the lines below to enable some popular extensions
107 # uncomment the lines below to enable some popular extensions
108 # (see 'hg help extensions' for more info)
108 # (see 'hg help extensions' for more info)
109 #
109 #
110 # histedit =
110 # histedit =
111 # rebase =
111 # rebase =
112 # uncommit =
112 # uncommit =
113 """,
113 """,
114 b'cloned': b"""# example repository config (see 'hg help config' for more info)
114 b'cloned': b"""# example repository config (see 'hg help config' for more info)
115 [paths]
115 [paths]
116 default = %s
116 default = %s
117
117
118 # path aliases to other clones of this repo in URLs or filesystem paths
118 # path aliases to other clones of this repo in URLs or filesystem paths
119 # (see 'hg help config.paths' for more info)
119 # (see 'hg help config.paths' for more info)
120 #
120 #
121 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
121 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
122 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
122 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
123 # my-clone = /home/jdoe/jdoes-clone
123 # my-clone = /home/jdoe/jdoes-clone
124
124
125 [ui]
125 [ui]
126 # name and email (local to this repository, optional), e.g.
126 # name and email (local to this repository, optional), e.g.
127 # username = Jane Doe <jdoe@example.com>
127 # username = Jane Doe <jdoe@example.com>
128 """,
128 """,
129 b'local': b"""# example repository config (see 'hg help config' for more info)
129 b'local': b"""# example repository config (see 'hg help config' for more info)
130 [paths]
130 [paths]
131 # path aliases to other clones of this repo in URLs or filesystem paths
131 # path aliases to other clones of this repo in URLs or filesystem paths
132 # (see 'hg help config.paths' for more info)
132 # (see 'hg help config.paths' for more info)
133 #
133 #
134 # default = http://example.com/hg/example-repo
134 # default = http://example.com/hg/example-repo
135 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
135 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
136 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
136 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
137 # my-clone = /home/jdoe/jdoes-clone
137 # my-clone = /home/jdoe/jdoes-clone
138
138
139 [ui]
139 [ui]
140 # name and email (local to this repository, optional), e.g.
140 # name and email (local to this repository, optional), e.g.
141 # username = Jane Doe <jdoe@example.com>
141 # username = Jane Doe <jdoe@example.com>
142 """,
142 """,
143 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
143 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
144
144
145 [ui]
145 [ui]
146 # uncomment to disable color in command output
146 # uncomment to disable color in command output
147 # (see 'hg help color' for details)
147 # (see 'hg help color' for details)
148 # color = never
148 # color = never
149
149
150 # uncomment to disable command output pagination
150 # uncomment to disable command output pagination
151 # (see 'hg help pager' for details)
151 # (see 'hg help pager' for details)
152 # paginate = never
152 # paginate = never
153
153
154 [extensions]
154 [extensions]
155 # uncomment the lines below to enable some popular extensions
155 # uncomment the lines below to enable some popular extensions
156 # (see 'hg help extensions' for more info)
156 # (see 'hg help extensions' for more info)
157 #
157 #
158 # blackbox =
158 # blackbox =
159 # churn =
159 # churn =
160 """,
160 """,
161 }
161 }
162
162
163
163
164 def _maybestrurl(maybebytes):
164 def _maybestrurl(maybebytes):
165 return pycompat.rapply(pycompat.strurl, maybebytes)
165 return pycompat.rapply(pycompat.strurl, maybebytes)
166
166
167
167
168 def _maybebytesurl(maybestr):
168 def _maybebytesurl(maybestr):
169 return pycompat.rapply(pycompat.bytesurl, maybestr)
169 return pycompat.rapply(pycompat.bytesurl, maybestr)
170
170
171
171
172 class httppasswordmgrdbproxy(object):
172 class httppasswordmgrdbproxy(object):
173 """Delays loading urllib2 until it's needed."""
173 """Delays loading urllib2 until it's needed."""
174
174
175 def __init__(self):
175 def __init__(self):
176 self._mgr = None
176 self._mgr = None
177
177
178 def _get_mgr(self):
178 def _get_mgr(self):
179 if self._mgr is None:
179 if self._mgr is None:
180 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
180 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
181 return self._mgr
181 return self._mgr
182
182
183 def add_password(self, realm, uris, user, passwd):
183 def add_password(self, realm, uris, user, passwd):
184 return self._get_mgr().add_password(
184 return self._get_mgr().add_password(
185 _maybestrurl(realm),
185 _maybestrurl(realm),
186 _maybestrurl(uris),
186 _maybestrurl(uris),
187 _maybestrurl(user),
187 _maybestrurl(user),
188 _maybestrurl(passwd),
188 _maybestrurl(passwd),
189 )
189 )
190
190
191 def find_user_password(self, realm, uri):
191 def find_user_password(self, realm, uri):
192 mgr = self._get_mgr()
192 mgr = self._get_mgr()
193 return _maybebytesurl(
193 return _maybebytesurl(
194 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
194 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
195 )
195 )
196
196
197
197
198 def _catchterm(*args):
198 def _catchterm(*args):
199 raise error.SignalInterrupt
199 raise error.SignalInterrupt
200
200
201
201
202 # unique object used to detect no default value has been provided when
202 # unique object used to detect no default value has been provided when
203 # retrieving configuration value.
203 # retrieving configuration value.
204 _unset = object()
204 _unset = object()
205
205
206 # _reqexithandlers: callbacks run at the end of a request
206 # _reqexithandlers: callbacks run at the end of a request
207 _reqexithandlers = []
207 _reqexithandlers = []
208
208
209
209
210 class ui(object):
210 class ui(object):
211 def __init__(self, src=None):
211 def __init__(self, src=None):
212 """Create a fresh new ui object if no src given
212 """Create a fresh new ui object if no src given
213
213
214 Use uimod.ui.load() to create a ui which knows global and user configs.
214 Use uimod.ui.load() to create a ui which knows global and user configs.
215 In most cases, you should use ui.copy() to create a copy of an existing
215 In most cases, you should use ui.copy() to create a copy of an existing
216 ui object.
216 ui object.
217 """
217 """
218 # _buffers: used for temporary capture of output
218 # _buffers: used for temporary capture of output
219 self._buffers = []
219 self._buffers = []
220 # 3-tuple describing how each buffer in the stack behaves.
220 # 3-tuple describing how each buffer in the stack behaves.
221 # Values are (capture stderr, capture subprocesses, apply labels).
221 # Values are (capture stderr, capture subprocesses, apply labels).
222 self._bufferstates = []
222 self._bufferstates = []
223 # When a buffer is active, defines whether we are expanding labels.
223 # When a buffer is active, defines whether we are expanding labels.
224 # This exists to prevent an extra list lookup.
224 # This exists to prevent an extra list lookup.
225 self._bufferapplylabels = None
225 self._bufferapplylabels = None
226 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
226 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
227 self._reportuntrusted = True
227 self._reportuntrusted = True
228 self._knownconfig = configitems.coreitems
228 self._knownconfig = configitems.coreitems
229 self._ocfg = config.config() # overlay
229 self._ocfg = config.config() # overlay
230 self._tcfg = config.config() # trusted
230 self._tcfg = config.config() # trusted
231 self._ucfg = config.config() # untrusted
231 self._ucfg = config.config() # untrusted
232 self._trustusers = set()
232 self._trustusers = set()
233 self._trustgroups = set()
233 self._trustgroups = set()
234 self.callhooks = True
234 self.callhooks = True
235 # Insecure server connections requested.
235 # Insecure server connections requested.
236 self.insecureconnections = False
236 self.insecureconnections = False
237 # Blocked time
237 # Blocked time
238 self.logblockedtimes = False
238 self.logblockedtimes = False
239 # color mode: see mercurial/color.py for possible value
239 # color mode: see mercurial/color.py for possible value
240 self._colormode = None
240 self._colormode = None
241 self._terminfoparams = {}
241 self._terminfoparams = {}
242 self._styles = {}
242 self._styles = {}
243 self._uninterruptible = False
243 self._uninterruptible = False
244
244
245 if src:
245 if src:
246 self._fout = src._fout
246 self._fout = src._fout
247 self._ferr = src._ferr
247 self._ferr = src._ferr
248 self._fin = src._fin
248 self._fin = src._fin
249 self._fmsg = src._fmsg
249 self._fmsg = src._fmsg
250 self._fmsgout = src._fmsgout
250 self._fmsgout = src._fmsgout
251 self._fmsgerr = src._fmsgerr
251 self._fmsgerr = src._fmsgerr
252 self._finoutredirected = src._finoutredirected
252 self._finoutredirected = src._finoutredirected
253 self._loggers = src._loggers.copy()
253 self._loggers = src._loggers.copy()
254 self.pageractive = src.pageractive
254 self.pageractive = src.pageractive
255 self._disablepager = src._disablepager
255 self._disablepager = src._disablepager
256 self._tweaked = src._tweaked
256 self._tweaked = src._tweaked
257
257
258 self._tcfg = src._tcfg.copy()
258 self._tcfg = src._tcfg.copy()
259 self._ucfg = src._ucfg.copy()
259 self._ucfg = src._ucfg.copy()
260 self._ocfg = src._ocfg.copy()
260 self._ocfg = src._ocfg.copy()
261 self._trustusers = src._trustusers.copy()
261 self._trustusers = src._trustusers.copy()
262 self._trustgroups = src._trustgroups.copy()
262 self._trustgroups = src._trustgroups.copy()
263 self.environ = src.environ
263 self.environ = src.environ
264 self.callhooks = src.callhooks
264 self.callhooks = src.callhooks
265 self.insecureconnections = src.insecureconnections
265 self.insecureconnections = src.insecureconnections
266 self._colormode = src._colormode
266 self._colormode = src._colormode
267 self._terminfoparams = src._terminfoparams.copy()
267 self._terminfoparams = src._terminfoparams.copy()
268 self._styles = src._styles.copy()
268 self._styles = src._styles.copy()
269
269
270 self.fixconfig()
270 self.fixconfig()
271
271
272 self.httppasswordmgrdb = src.httppasswordmgrdb
272 self.httppasswordmgrdb = src.httppasswordmgrdb
273 self._blockedtimes = src._blockedtimes
273 self._blockedtimes = src._blockedtimes
274 else:
274 else:
275 self._fout = procutil.stdout
275 self._fout = procutil.stdout
276 self._ferr = procutil.stderr
276 self._ferr = procutil.stderr
277 self._fin = procutil.stdin
277 self._fin = procutil.stdin
278 self._fmsg = None
278 self._fmsg = None
279 self._fmsgout = self.fout # configurable
279 self._fmsgout = self.fout # configurable
280 self._fmsgerr = self.ferr # configurable
280 self._fmsgerr = self.ferr # configurable
281 self._finoutredirected = False
281 self._finoutredirected = False
282 self._loggers = {}
282 self._loggers = {}
283 self.pageractive = False
283 self.pageractive = False
284 self._disablepager = False
284 self._disablepager = False
285 self._tweaked = False
285 self._tweaked = False
286
286
287 # shared read-only environment
287 # shared read-only environment
288 self.environ = encoding.environ
288 self.environ = encoding.environ
289
289
290 self.httppasswordmgrdb = httppasswordmgrdbproxy()
290 self.httppasswordmgrdb = httppasswordmgrdbproxy()
291 self._blockedtimes = collections.defaultdict(int)
291 self._blockedtimes = collections.defaultdict(int)
292
292
293 allowed = self.configlist(b'experimental', b'exportableenviron')
293 allowed = self.configlist(b'experimental', b'exportableenviron')
294 if b'*' in allowed:
294 if b'*' in allowed:
295 self._exportableenviron = self.environ
295 self._exportableenviron = self.environ
296 else:
296 else:
297 self._exportableenviron = {}
297 self._exportableenviron = {}
298 for k in allowed:
298 for k in allowed:
299 if k in self.environ:
299 if k in self.environ:
300 self._exportableenviron[k] = self.environ[k]
300 self._exportableenviron[k] = self.environ[k]
301
301
302 @classmethod
302 @classmethod
303 def load(cls):
303 def load(cls):
304 """Create a ui and load global and user configs"""
304 """Create a ui and load global and user configs"""
305 u = cls()
305 u = cls()
306 # we always trust global config files and environment variables
306 # we always trust global config files and environment variables
307 for t, f in rcutil.rccomponents():
307 for t, f in rcutil.rccomponents():
308 if t == b'path':
308 if t == b'path':
309 u.readconfig(f, trust=True)
309 u.readconfig(f, trust=True)
310 elif t == b'items':
310 elif t == b'items':
311 sections = set()
311 sections = set()
312 for section, name, value, source in f:
312 for section, name, value, source in f:
313 # do not set u._ocfg
313 # do not set u._ocfg
314 # XXX clean this up once immutable config object is a thing
314 # XXX clean this up once immutable config object is a thing
315 u._tcfg.set(section, name, value, source)
315 u._tcfg.set(section, name, value, source)
316 u._ucfg.set(section, name, value, source)
316 u._ucfg.set(section, name, value, source)
317 sections.add(section)
317 sections.add(section)
318 for section in sections:
318 for section in sections:
319 u.fixconfig(section=section)
319 u.fixconfig(section=section)
320 else:
320 else:
321 raise error.ProgrammingError(b'unknown rctype: %s' % t)
321 raise error.ProgrammingError(b'unknown rctype: %s' % t)
322 u._maybetweakdefaults()
322 u._maybetweakdefaults()
323 return u
323 return u
324
324
325 def _maybetweakdefaults(self):
325 def _maybetweakdefaults(self):
326 if not self.configbool(b'ui', b'tweakdefaults'):
326 if not self.configbool(b'ui', b'tweakdefaults'):
327 return
327 return
328 if self._tweaked or self.plain(b'tweakdefaults'):
328 if self._tweaked or self.plain(b'tweakdefaults'):
329 return
329 return
330
330
331 # Note: it is SUPER IMPORTANT that you set self._tweaked to
331 # Note: it is SUPER IMPORTANT that you set self._tweaked to
332 # True *before* any calls to setconfig(), otherwise you'll get
332 # True *before* any calls to setconfig(), otherwise you'll get
333 # infinite recursion between setconfig and this method.
333 # infinite recursion between setconfig and this method.
334 #
334 #
335 # TODO: We should extract an inner method in setconfig() to
335 # TODO: We should extract an inner method in setconfig() to
336 # avoid this weirdness.
336 # avoid this weirdness.
337 self._tweaked = True
337 self._tweaked = True
338 tmpcfg = config.config()
338 tmpcfg = config.config()
339 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
339 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
340 for section in tmpcfg:
340 for section in tmpcfg:
341 for name, value in tmpcfg.items(section):
341 for name, value in tmpcfg.items(section):
342 if not self.hasconfig(section, name):
342 if not self.hasconfig(section, name):
343 self.setconfig(section, name, value, b"<tweakdefaults>")
343 self.setconfig(section, name, value, b"<tweakdefaults>")
344
344
345 def copy(self):
345 def copy(self):
346 return self.__class__(self)
346 return self.__class__(self)
347
347
348 def resetstate(self):
348 def resetstate(self):
349 """Clear internal state that shouldn't persist across commands"""
349 """Clear internal state that shouldn't persist across commands"""
350 if self._progbar:
350 if self._progbar:
351 self._progbar.resetstate() # reset last-print time of progress bar
351 self._progbar.resetstate() # reset last-print time of progress bar
352 self.httppasswordmgrdb = httppasswordmgrdbproxy()
352 self.httppasswordmgrdb = httppasswordmgrdbproxy()
353
353
354 @contextlib.contextmanager
354 @contextlib.contextmanager
355 def timeblockedsection(self, key):
355 def timeblockedsection(self, key):
356 # this is open-coded below - search for timeblockedsection to find them
356 # this is open-coded below - search for timeblockedsection to find them
357 starttime = util.timer()
357 starttime = util.timer()
358 try:
358 try:
359 yield
359 yield
360 finally:
360 finally:
361 self._blockedtimes[key + b'_blocked'] += (
361 self._blockedtimes[key + b'_blocked'] += (
362 util.timer() - starttime
362 util.timer() - starttime
363 ) * 1000
363 ) * 1000
364
364
365 @contextlib.contextmanager
365 @contextlib.contextmanager
366 def uninterruptible(self):
366 def uninterruptible(self):
367 """Mark an operation as unsafe.
367 """Mark an operation as unsafe.
368
368
369 Most operations on a repository are safe to interrupt, but a
369 Most operations on a repository are safe to interrupt, but a
370 few are risky (for example repair.strip). This context manager
370 few are risky (for example repair.strip). This context manager
371 lets you advise Mercurial that something risky is happening so
371 lets you advise Mercurial that something risky is happening so
372 that control-C etc can be blocked if desired.
372 that control-C etc can be blocked if desired.
373 """
373 """
374 enabled = self.configbool(b'experimental', b'nointerrupt')
374 enabled = self.configbool(b'experimental', b'nointerrupt')
375 if enabled and self.configbool(
375 if enabled and self.configbool(
376 b'experimental', b'nointerrupt-interactiveonly'
376 b'experimental', b'nointerrupt-interactiveonly'
377 ):
377 ):
378 enabled = self.interactive()
378 enabled = self.interactive()
379 if self._uninterruptible or not enabled:
379 if self._uninterruptible or not enabled:
380 # if nointerrupt support is turned off, the process isn't
380 # if nointerrupt support is turned off, the process isn't
381 # interactive, or we're already in an uninterruptible
381 # interactive, or we're already in an uninterruptible
382 # block, do nothing.
382 # block, do nothing.
383 yield
383 yield
384 return
384 return
385
385
386 def warn():
386 def warn():
387 self.warn(_(b"shutting down cleanly\n"))
387 self.warn(_(b"shutting down cleanly\n"))
388 self.warn(
388 self.warn(
389 _(b"press ^C again to terminate immediately (dangerous)\n")
389 _(b"press ^C again to terminate immediately (dangerous)\n")
390 )
390 )
391 return True
391 return True
392
392
393 with procutil.uninterruptible(warn):
393 with procutil.uninterruptible(warn):
394 try:
394 try:
395 self._uninterruptible = True
395 self._uninterruptible = True
396 yield
396 yield
397 finally:
397 finally:
398 self._uninterruptible = False
398 self._uninterruptible = False
399
399
400 def formatter(self, topic, opts):
400 def formatter(self, topic, opts):
401 return formatter.formatter(self, self, topic, opts)
401 return formatter.formatter(self, self, topic, opts)
402
402
403 def _trusted(self, fp, f):
403 def _trusted(self, fp, f):
404 st = util.fstat(fp)
404 st = util.fstat(fp)
405 if util.isowner(st):
405 if util.isowner(st):
406 return True
406 return True
407
407
408 tusers, tgroups = self._trustusers, self._trustgroups
408 tusers, tgroups = self._trustusers, self._trustgroups
409 if b'*' in tusers or b'*' in tgroups:
409 if b'*' in tusers or b'*' in tgroups:
410 return True
410 return True
411
411
412 user = util.username(st.st_uid)
412 user = util.username(st.st_uid)
413 group = util.groupname(st.st_gid)
413 group = util.groupname(st.st_gid)
414 if user in tusers or group in tgroups or user == util.username():
414 if user in tusers or group in tgroups or user == util.username():
415 return True
415 return True
416
416
417 if self._reportuntrusted:
417 if self._reportuntrusted:
418 self.warn(
418 self.warn(
419 _(
419 _(
420 b'not trusting file %s from untrusted '
420 b'not trusting file %s from untrusted '
421 b'user %s, group %s\n'
421 b'user %s, group %s\n'
422 )
422 )
423 % (f, user, group)
423 % (f, user, group)
424 )
424 )
425 return False
425 return False
426
426
427 def readconfig(
427 def readconfig(
428 self, filename, root=None, trust=False, sections=None, remap=None
428 self, filename, root=None, trust=False, sections=None, remap=None
429 ):
429 ):
430 try:
430 try:
431 fp = open(filename, 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 Progress reports via stderr are normally cleared before writing as
1076 stdout and stderr go to the same terminal. This can be skipped with
1077 the optional keyword argument "keepprogressbar". The progress bar
1078 will continue to occupy a partial line on stderr in that case.
1079 This functionality is intended when Mercurial acts as data source
1080 in a pipe.
1081
1075 When labeling output for a specific command, a label of
1082 When labeling output for a specific command, a label of
1076 "cmdname.type" is recommended. For example, status issues
1083 "cmdname.type" is recommended. For example, status issues
1077 a label of "status.modified" for modified files.
1084 a label of "status.modified" for modified files.
1078 '''
1085 '''
1079 dest = self._fout
1086 dest = self._fout
1080
1087
1081 # inlined _write() for speed
1088 # inlined _write() for speed
1082 if self._buffers:
1089 if self._buffers:
1083 label = opts.get(r'label', b'')
1090 label = opts.get(r'label', b'')
1084 if label and self._bufferapplylabels:
1091 if label and self._bufferapplylabels:
1085 self._buffers[-1].extend(self.label(a, label) for a in args)
1092 self._buffers[-1].extend(self.label(a, label) for a in args)
1086 else:
1093 else:
1087 self._buffers[-1].extend(args)
1094 self._buffers[-1].extend(args)
1088 return
1095 return
1089
1096
1090 # inliend _writenobuf() for speed
1097 # inlined _writenobuf() for speed
1098 if not opts.get(r'keepprogressbar', False):
1091 self._progclear()
1099 self._progclear()
1092 msg = b''.join(args)
1100 msg = b''.join(args)
1093
1101
1094 # opencode timeblockedsection because this is a critical path
1102 # opencode timeblockedsection because this is a critical path
1095 starttime = util.timer()
1103 starttime = util.timer()
1096 try:
1104 try:
1097 if self._colormode == b'win32':
1105 if self._colormode == b'win32':
1098 # windows color printing is its own can of crab, defer to
1106 # windows color printing is its own can of crab, defer to
1099 # the color module and that is it.
1107 # the color module and that is it.
1100 color.win32print(self, dest.write, msg, **opts)
1108 color.win32print(self, dest.write, msg, **opts)
1101 else:
1109 else:
1102 if self._colormode is not None:
1110 if self._colormode is not None:
1103 label = opts.get(r'label', b'')
1111 label = opts.get(r'label', b'')
1104 msg = self.label(msg, label)
1112 msg = self.label(msg, label)
1105 dest.write(msg)
1113 dest.write(msg)
1106 except IOError as err:
1114 except IOError as err:
1107 raise error.StdioError(err)
1115 raise error.StdioError(err)
1108 finally:
1116 finally:
1109 self._blockedtimes[b'stdio_blocked'] += (
1117 self._blockedtimes[b'stdio_blocked'] += (
1110 util.timer() - starttime
1118 util.timer() - starttime
1111 ) * 1000
1119 ) * 1000
1112
1120
1113 def write_err(self, *args, **opts):
1121 def write_err(self, *args, **opts):
1114 self._write(self._ferr, *args, **opts)
1122 self._write(self._ferr, *args, **opts)
1115
1123
1116 def _write(self, dest, *args, **opts):
1124 def _write(self, dest, *args, **opts):
1117 # update write() as well if you touch this code
1125 # update write() as well if you touch this code
1118 if self._isbuffered(dest):
1126 if self._isbuffered(dest):
1119 label = opts.get(r'label', b'')
1127 label = opts.get(r'label', b'')
1120 if label and self._bufferapplylabels:
1128 if label and self._bufferapplylabels:
1121 self._buffers[-1].extend(self.label(a, label) for a in args)
1129 self._buffers[-1].extend(self.label(a, label) for a in args)
1122 else:
1130 else:
1123 self._buffers[-1].extend(args)
1131 self._buffers[-1].extend(args)
1124 else:
1132 else:
1125 self._writenobuf(dest, *args, **opts)
1133 self._writenobuf(dest, *args, **opts)
1126
1134
1127 def _writenobuf(self, dest, *args, **opts):
1135 def _writenobuf(self, dest, *args, **opts):
1128 # update write() as well if you touch this code
1136 # update write() as well if you touch this code
1137 if not opts.get(r'keepprogressbar', False):
1129 self._progclear()
1138 self._progclear()
1130 msg = b''.join(args)
1139 msg = b''.join(args)
1131
1140
1132 # opencode timeblockedsection because this is a critical path
1141 # opencode timeblockedsection because this is a critical path
1133 starttime = util.timer()
1142 starttime = util.timer()
1134 try:
1143 try:
1135 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1144 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1136 self._fout.flush()
1145 self._fout.flush()
1137 if getattr(dest, 'structured', False):
1146 if getattr(dest, 'structured', False):
1138 # channel for machine-readable output with metadata, where
1147 # channel for machine-readable output with metadata, where
1139 # no extra colorization is necessary.
1148 # no extra colorization is necessary.
1140 dest.write(msg, **opts)
1149 dest.write(msg, **opts)
1141 elif self._colormode == b'win32':
1150 elif self._colormode == b'win32':
1142 # windows color printing is its own can of crab, defer to
1151 # windows color printing is its own can of crab, defer to
1143 # the color module and that is it.
1152 # the color module and that is it.
1144 color.win32print(self, dest.write, msg, **opts)
1153 color.win32print(self, dest.write, msg, **opts)
1145 else:
1154 else:
1146 if self._colormode is not None:
1155 if self._colormode is not None:
1147 label = opts.get(r'label', b'')
1156 label = opts.get(r'label', b'')
1148 msg = self.label(msg, label)
1157 msg = self.label(msg, label)
1149 dest.write(msg)
1158 dest.write(msg)
1150 # stderr may be buffered under win32 when redirected to files,
1159 # stderr may be buffered under win32 when redirected to files,
1151 # including stdout.
1160 # including stdout.
1152 if dest is self._ferr and not getattr(self._ferr, 'closed', False):
1161 if dest is self._ferr and not getattr(self._ferr, 'closed', False):
1153 dest.flush()
1162 dest.flush()
1154 except IOError as err:
1163 except IOError as err:
1155 if dest is self._ferr and err.errno in (
1164 if dest is self._ferr and err.errno in (
1156 errno.EPIPE,
1165 errno.EPIPE,
1157 errno.EIO,
1166 errno.EIO,
1158 errno.EBADF,
1167 errno.EBADF,
1159 ):
1168 ):
1160 # no way to report the error, so ignore it
1169 # no way to report the error, so ignore it
1161 return
1170 return
1162 raise error.StdioError(err)
1171 raise error.StdioError(err)
1163 finally:
1172 finally:
1164 self._blockedtimes[b'stdio_blocked'] += (
1173 self._blockedtimes[b'stdio_blocked'] += (
1165 util.timer() - starttime
1174 util.timer() - starttime
1166 ) * 1000
1175 ) * 1000
1167
1176
1168 def _writemsg(self, dest, *args, **opts):
1177 def _writemsg(self, dest, *args, **opts):
1169 _writemsgwith(self._write, dest, *args, **opts)
1178 _writemsgwith(self._write, dest, *args, **opts)
1170
1179
1171 def _writemsgnobuf(self, dest, *args, **opts):
1180 def _writemsgnobuf(self, dest, *args, **opts):
1172 _writemsgwith(self._writenobuf, dest, *args, **opts)
1181 _writemsgwith(self._writenobuf, dest, *args, **opts)
1173
1182
1174 def flush(self):
1183 def flush(self):
1175 # opencode timeblockedsection because this is a critical path
1184 # opencode timeblockedsection because this is a critical path
1176 starttime = util.timer()
1185 starttime = util.timer()
1177 try:
1186 try:
1178 try:
1187 try:
1179 self._fout.flush()
1188 self._fout.flush()
1180 except IOError as err:
1189 except IOError as err:
1181 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1190 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1182 raise error.StdioError(err)
1191 raise error.StdioError(err)
1183 finally:
1192 finally:
1184 try:
1193 try:
1185 self._ferr.flush()
1194 self._ferr.flush()
1186 except IOError as err:
1195 except IOError as err:
1187 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1196 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1188 raise error.StdioError(err)
1197 raise error.StdioError(err)
1189 finally:
1198 finally:
1190 self._blockedtimes[b'stdio_blocked'] += (
1199 self._blockedtimes[b'stdio_blocked'] += (
1191 util.timer() - starttime
1200 util.timer() - starttime
1192 ) * 1000
1201 ) * 1000
1193
1202
1194 def _isatty(self, fh):
1203 def _isatty(self, fh):
1195 if self.configbool(b'ui', b'nontty'):
1204 if self.configbool(b'ui', b'nontty'):
1196 return False
1205 return False
1197 return procutil.isatty(fh)
1206 return procutil.isatty(fh)
1198
1207
1199 def protectfinout(self):
1208 def protectfinout(self):
1200 """Duplicate ui streams and redirect original if they are stdio
1209 """Duplicate ui streams and redirect original if they are stdio
1201
1210
1202 Returns (fin, fout) which point to the original ui fds, but may be
1211 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
1212 copy of them. The returned streams can be considered "owned" in that
1204 print(), exec(), etc. never reach to them.
1213 print(), exec(), etc. never reach to them.
1205 """
1214 """
1206 if self._finoutredirected:
1215 if self._finoutredirected:
1207 # if already redirected, protectstdio() would just create another
1216 # if already redirected, protectstdio() would just create another
1208 # nullfd pair, which is equivalent to returning self._fin/_fout.
1217 # nullfd pair, which is equivalent to returning self._fin/_fout.
1209 return self._fin, self._fout
1218 return self._fin, self._fout
1210 fin, fout = procutil.protectstdio(self._fin, self._fout)
1219 fin, fout = procutil.protectstdio(self._fin, self._fout)
1211 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1220 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1212 return fin, fout
1221 return fin, fout
1213
1222
1214 def restorefinout(self, fin, fout):
1223 def restorefinout(self, fin, fout):
1215 """Restore ui streams from possibly duplicated (fin, fout)"""
1224 """Restore ui streams from possibly duplicated (fin, fout)"""
1216 if (fin, fout) == (self._fin, self._fout):
1225 if (fin, fout) == (self._fin, self._fout):
1217 return
1226 return
1218 procutil.restorestdio(self._fin, self._fout, fin, fout)
1227 procutil.restorestdio(self._fin, self._fout, fin, fout)
1219 # protectfinout() won't create more than one duplicated streams,
1228 # protectfinout() won't create more than one duplicated streams,
1220 # so we can just turn the redirection flag off.
1229 # so we can just turn the redirection flag off.
1221 self._finoutredirected = False
1230 self._finoutredirected = False
1222
1231
1223 @contextlib.contextmanager
1232 @contextlib.contextmanager
1224 def protectedfinout(self):
1233 def protectedfinout(self):
1225 """Run code block with protected standard streams"""
1234 """Run code block with protected standard streams"""
1226 fin, fout = self.protectfinout()
1235 fin, fout = self.protectfinout()
1227 try:
1236 try:
1228 yield fin, fout
1237 yield fin, fout
1229 finally:
1238 finally:
1230 self.restorefinout(fin, fout)
1239 self.restorefinout(fin, fout)
1231
1240
1232 def disablepager(self):
1241 def disablepager(self):
1233 self._disablepager = True
1242 self._disablepager = True
1234
1243
1235 def pager(self, command):
1244 def pager(self, command):
1236 """Start a pager for subsequent command output.
1245 """Start a pager for subsequent command output.
1237
1246
1238 Commands which produce a long stream of output should call
1247 Commands which produce a long stream of output should call
1239 this function to activate the user's preferred pagination
1248 this function to activate the user's preferred pagination
1240 mechanism (which may be no pager). Calling this function
1249 mechanism (which may be no pager). Calling this function
1241 precludes any future use of interactive functionality, such as
1250 precludes any future use of interactive functionality, such as
1242 prompting the user or activating curses.
1251 prompting the user or activating curses.
1243
1252
1244 Args:
1253 Args:
1245 command: The full, non-aliased name of the command. That is, "log"
1254 command: The full, non-aliased name of the command. That is, "log"
1246 not "history, "summary" not "summ", etc.
1255 not "history, "summary" not "summ", etc.
1247 """
1256 """
1248 if self._disablepager or self.pageractive:
1257 if self._disablepager or self.pageractive:
1249 # how pager should do is already determined
1258 # how pager should do is already determined
1250 return
1259 return
1251
1260
1252 if not command.startswith(b'internal-always-') and (
1261 if not command.startswith(b'internal-always-') and (
1253 # explicit --pager=on (= 'internal-always-' prefix) should
1262 # explicit --pager=on (= 'internal-always-' prefix) should
1254 # take precedence over disabling factors below
1263 # take precedence over disabling factors below
1255 command in self.configlist(b'pager', b'ignore')
1264 command in self.configlist(b'pager', b'ignore')
1256 or not self.configbool(b'ui', b'paginate')
1265 or not self.configbool(b'ui', b'paginate')
1257 or not self.configbool(b'pager', b'attend-' + command, True)
1266 or not self.configbool(b'pager', b'attend-' + command, True)
1258 or encoding.environ.get(b'TERM') == b'dumb'
1267 or encoding.environ.get(b'TERM') == b'dumb'
1259 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1268 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1260 # formatted() will need some adjustment.
1269 # formatted() will need some adjustment.
1261 or not self.formatted()
1270 or not self.formatted()
1262 or self.plain()
1271 or self.plain()
1263 or self._buffers
1272 or self._buffers
1264 # TODO: expose debugger-enabled on the UI object
1273 # TODO: expose debugger-enabled on the UI object
1265 or b'--debugger' in pycompat.sysargv
1274 or b'--debugger' in pycompat.sysargv
1266 ):
1275 ):
1267 # We only want to paginate if the ui appears to be
1276 # We only want to paginate if the ui appears to be
1268 # interactive, the user didn't say HGPLAIN or
1277 # interactive, the user didn't say HGPLAIN or
1269 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1278 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1270 return
1279 return
1271
1280
1272 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1281 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1273 if not pagercmd:
1282 if not pagercmd:
1274 return
1283 return
1275
1284
1276 pagerenv = {}
1285 pagerenv = {}
1277 for name, value in rcutil.defaultpagerenv().items():
1286 for name, value in rcutil.defaultpagerenv().items():
1278 if name not in encoding.environ:
1287 if name not in encoding.environ:
1279 pagerenv[name] = value
1288 pagerenv[name] = value
1280
1289
1281 self.debug(
1290 self.debug(
1282 b'starting pager for command %s\n' % stringutil.pprint(command)
1291 b'starting pager for command %s\n' % stringutil.pprint(command)
1283 )
1292 )
1284 self.flush()
1293 self.flush()
1285
1294
1286 wasformatted = self.formatted()
1295 wasformatted = self.formatted()
1287 if util.safehasattr(signal, b"SIGPIPE"):
1296 if util.safehasattr(signal, b"SIGPIPE"):
1288 signal.signal(signal.SIGPIPE, _catchterm)
1297 signal.signal(signal.SIGPIPE, _catchterm)
1289 if self._runpager(pagercmd, pagerenv):
1298 if self._runpager(pagercmd, pagerenv):
1290 self.pageractive = True
1299 self.pageractive = True
1291 # Preserve the formatted-ness of the UI. This is important
1300 # Preserve the formatted-ness of the UI. This is important
1292 # because we mess with stdout, which might confuse
1301 # because we mess with stdout, which might confuse
1293 # auto-detection of things being formatted.
1302 # auto-detection of things being formatted.
1294 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1303 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1295 self.setconfig(b'ui', b'interactive', False, b'pager')
1304 self.setconfig(b'ui', b'interactive', False, b'pager')
1296
1305
1297 # If pagermode differs from color.mode, reconfigure color now that
1306 # If pagermode differs from color.mode, reconfigure color now that
1298 # pageractive is set.
1307 # pageractive is set.
1299 cm = self._colormode
1308 cm = self._colormode
1300 if cm != self.config(b'color', b'pagermode', cm):
1309 if cm != self.config(b'color', b'pagermode', cm):
1301 color.setup(self)
1310 color.setup(self)
1302 else:
1311 else:
1303 # If the pager can't be spawned in dispatch when --pager=on is
1312 # 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
1313 # given, don't try again when the command runs, to avoid a duplicate
1305 # warning about a missing pager command.
1314 # warning about a missing pager command.
1306 self.disablepager()
1315 self.disablepager()
1307
1316
1308 def _runpager(self, command, env=None):
1317 def _runpager(self, command, env=None):
1309 """Actually start the pager and set up file descriptors.
1318 """Actually start the pager and set up file descriptors.
1310
1319
1311 This is separate in part so that extensions (like chg) can
1320 This is separate in part so that extensions (like chg) can
1312 override how a pager is invoked.
1321 override how a pager is invoked.
1313 """
1322 """
1314 if command == b'cat':
1323 if command == b'cat':
1315 # Save ourselves some work.
1324 # Save ourselves some work.
1316 return False
1325 return False
1317 # If the command doesn't contain any of these characters, we
1326 # If the command doesn't contain any of these characters, we
1318 # assume it's a binary and exec it directly. This means for
1327 # assume it's a binary and exec it directly. This means for
1319 # simple pager command configurations, we can degrade
1328 # simple pager command configurations, we can degrade
1320 # gracefully and tell the user about their broken pager.
1329 # gracefully and tell the user about their broken pager.
1321 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1330 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1322
1331
1323 if pycompat.iswindows and not shell:
1332 if pycompat.iswindows and not shell:
1324 # Window's built-in `more` cannot be invoked with shell=False, but
1333 # Window's built-in `more` cannot be invoked with shell=False, but
1325 # its `more.com` can. Hide this implementation detail from the
1334 # its `more.com` can. Hide this implementation detail from the
1326 # user so we can also get sane bad PAGER behavior. MSYS has
1335 # 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
1336 # `more.exe`, so do a cmd.exe style resolution of the executable to
1328 # determine which one to use.
1337 # determine which one to use.
1329 fullcmd = procutil.findexe(command)
1338 fullcmd = procutil.findexe(command)
1330 if not fullcmd:
1339 if not fullcmd:
1331 self.warn(
1340 self.warn(
1332 _(b"missing pager command '%s', skipping pager\n") % command
1341 _(b"missing pager command '%s', skipping pager\n") % command
1333 )
1342 )
1334 return False
1343 return False
1335
1344
1336 command = fullcmd
1345 command = fullcmd
1337
1346
1338 try:
1347 try:
1339 pager = subprocess.Popen(
1348 pager = subprocess.Popen(
1340 procutil.tonativestr(command),
1349 procutil.tonativestr(command),
1341 shell=shell,
1350 shell=shell,
1342 bufsize=-1,
1351 bufsize=-1,
1343 close_fds=procutil.closefds,
1352 close_fds=procutil.closefds,
1344 stdin=subprocess.PIPE,
1353 stdin=subprocess.PIPE,
1345 stdout=procutil.stdout,
1354 stdout=procutil.stdout,
1346 stderr=procutil.stderr,
1355 stderr=procutil.stderr,
1347 env=procutil.tonativeenv(procutil.shellenviron(env)),
1356 env=procutil.tonativeenv(procutil.shellenviron(env)),
1348 )
1357 )
1349 except OSError as e:
1358 except OSError as e:
1350 if e.errno == errno.ENOENT and not shell:
1359 if e.errno == errno.ENOENT and not shell:
1351 self.warn(
1360 self.warn(
1352 _(b"missing pager command '%s', skipping pager\n") % command
1361 _(b"missing pager command '%s', skipping pager\n") % command
1353 )
1362 )
1354 return False
1363 return False
1355 raise
1364 raise
1356
1365
1357 # back up original file descriptors
1366 # back up original file descriptors
1358 stdoutfd = os.dup(procutil.stdout.fileno())
1367 stdoutfd = os.dup(procutil.stdout.fileno())
1359 stderrfd = os.dup(procutil.stderr.fileno())
1368 stderrfd = os.dup(procutil.stderr.fileno())
1360
1369
1361 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1370 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1362 if self._isatty(procutil.stderr):
1371 if self._isatty(procutil.stderr):
1363 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1372 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1364
1373
1365 @self.atexit
1374 @self.atexit
1366 def killpager():
1375 def killpager():
1367 if util.safehasattr(signal, b"SIGINT"):
1376 if util.safehasattr(signal, b"SIGINT"):
1368 signal.signal(signal.SIGINT, signal.SIG_IGN)
1377 signal.signal(signal.SIGINT, signal.SIG_IGN)
1369 # restore original fds, closing pager.stdin copies in the process
1378 # restore original fds, closing pager.stdin copies in the process
1370 os.dup2(stdoutfd, procutil.stdout.fileno())
1379 os.dup2(stdoutfd, procutil.stdout.fileno())
1371 os.dup2(stderrfd, procutil.stderr.fileno())
1380 os.dup2(stderrfd, procutil.stderr.fileno())
1372 pager.stdin.close()
1381 pager.stdin.close()
1373 pager.wait()
1382 pager.wait()
1374
1383
1375 return True
1384 return True
1376
1385
1377 @property
1386 @property
1378 def _exithandlers(self):
1387 def _exithandlers(self):
1379 return _reqexithandlers
1388 return _reqexithandlers
1380
1389
1381 def atexit(self, func, *args, **kwargs):
1390 def atexit(self, func, *args, **kwargs):
1382 '''register a function to run after dispatching a request
1391 '''register a function to run after dispatching a request
1383
1392
1384 Handlers do not stay registered across request boundaries.'''
1393 Handlers do not stay registered across request boundaries.'''
1385 self._exithandlers.append((func, args, kwargs))
1394 self._exithandlers.append((func, args, kwargs))
1386 return func
1395 return func
1387
1396
1388 def interface(self, feature):
1397 def interface(self, feature):
1389 """what interface to use for interactive console features?
1398 """what interface to use for interactive console features?
1390
1399
1391 The interface is controlled by the value of `ui.interface` but also by
1400 The interface is controlled by the value of `ui.interface` but also by
1392 the value of feature-specific configuration. For example:
1401 the value of feature-specific configuration. For example:
1393
1402
1394 ui.interface.histedit = text
1403 ui.interface.histedit = text
1395 ui.interface.chunkselector = curses
1404 ui.interface.chunkselector = curses
1396
1405
1397 Here the features are "histedit" and "chunkselector".
1406 Here the features are "histedit" and "chunkselector".
1398
1407
1399 The configuration above means that the default interfaces for commands
1408 The configuration above means that the default interfaces for commands
1400 is curses, the interface for histedit is text and the interface for
1409 is curses, the interface for histedit is text and the interface for
1401 selecting chunk is crecord (the best curses interface available).
1410 selecting chunk is crecord (the best curses interface available).
1402
1411
1403 Consider the following example:
1412 Consider the following example:
1404 ui.interface = curses
1413 ui.interface = curses
1405 ui.interface.histedit = text
1414 ui.interface.histedit = text
1406
1415
1407 Then histedit will use the text interface and chunkselector will use
1416 Then histedit will use the text interface and chunkselector will use
1408 the default curses interface (crecord at the moment).
1417 the default curses interface (crecord at the moment).
1409 """
1418 """
1410 alldefaults = frozenset([b"text", b"curses"])
1419 alldefaults = frozenset([b"text", b"curses"])
1411
1420
1412 featureinterfaces = {
1421 featureinterfaces = {
1413 b"chunkselector": [b"text", b"curses",],
1422 b"chunkselector": [b"text", b"curses",],
1414 b"histedit": [b"text", b"curses",],
1423 b"histedit": [b"text", b"curses",],
1415 }
1424 }
1416
1425
1417 # Feature-specific interface
1426 # Feature-specific interface
1418 if feature not in featureinterfaces.keys():
1427 if feature not in featureinterfaces.keys():
1419 # Programming error, not user error
1428 # Programming error, not user error
1420 raise ValueError(b"Unknown feature requested %s" % feature)
1429 raise ValueError(b"Unknown feature requested %s" % feature)
1421
1430
1422 availableinterfaces = frozenset(featureinterfaces[feature])
1431 availableinterfaces = frozenset(featureinterfaces[feature])
1423 if alldefaults > availableinterfaces:
1432 if alldefaults > availableinterfaces:
1424 # Programming error, not user error. We need a use case to
1433 # Programming error, not user error. We need a use case to
1425 # define the right thing to do here.
1434 # define the right thing to do here.
1426 raise ValueError(
1435 raise ValueError(
1427 b"Feature %s does not handle all default interfaces" % feature
1436 b"Feature %s does not handle all default interfaces" % feature
1428 )
1437 )
1429
1438
1430 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1439 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1431 return b"text"
1440 return b"text"
1432
1441
1433 # Default interface for all the features
1442 # Default interface for all the features
1434 defaultinterface = b"text"
1443 defaultinterface = b"text"
1435 i = self.config(b"ui", b"interface")
1444 i = self.config(b"ui", b"interface")
1436 if i in alldefaults:
1445 if i in alldefaults:
1437 defaultinterface = i
1446 defaultinterface = i
1438
1447
1439 choseninterface = defaultinterface
1448 choseninterface = defaultinterface
1440 f = self.config(b"ui", b"interface.%s" % feature)
1449 f = self.config(b"ui", b"interface.%s" % feature)
1441 if f in availableinterfaces:
1450 if f in availableinterfaces:
1442 choseninterface = f
1451 choseninterface = f
1443
1452
1444 if i is not None and defaultinterface != i:
1453 if i is not None and defaultinterface != i:
1445 if f is not None:
1454 if f is not None:
1446 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1455 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1447 else:
1456 else:
1448 self.warn(
1457 self.warn(
1449 _(b"invalid value for ui.interface: %s (using %s)\n")
1458 _(b"invalid value for ui.interface: %s (using %s)\n")
1450 % (i, choseninterface)
1459 % (i, choseninterface)
1451 )
1460 )
1452 if f is not None and choseninterface != f:
1461 if f is not None and choseninterface != f:
1453 self.warn(
1462 self.warn(
1454 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1463 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1455 % (feature, f, choseninterface)
1464 % (feature, f, choseninterface)
1456 )
1465 )
1457
1466
1458 return choseninterface
1467 return choseninterface
1459
1468
1460 def interactive(self):
1469 def interactive(self):
1461 '''is interactive input allowed?
1470 '''is interactive input allowed?
1462
1471
1463 An interactive session is a session where input can be reasonably read
1472 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
1473 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
1474 from stdin should fail with an error, unless a sensible default has been
1466 specified.
1475 specified.
1467
1476
1468 Interactiveness is triggered by the value of the `ui.interactive'
1477 Interactiveness is triggered by the value of the `ui.interactive'
1469 configuration variable or - if it is unset - when `sys.stdin' points
1478 configuration variable or - if it is unset - when `sys.stdin' points
1470 to a terminal device.
1479 to a terminal device.
1471
1480
1472 This function refers to input only; for output, see `ui.formatted()'.
1481 This function refers to input only; for output, see `ui.formatted()'.
1473 '''
1482 '''
1474 i = self.configbool(b"ui", b"interactive")
1483 i = self.configbool(b"ui", b"interactive")
1475 if i is None:
1484 if i is None:
1476 # some environments replace stdin without implementing isatty
1485 # some environments replace stdin without implementing isatty
1477 # usually those are non-interactive
1486 # usually those are non-interactive
1478 return self._isatty(self._fin)
1487 return self._isatty(self._fin)
1479
1488
1480 return i
1489 return i
1481
1490
1482 def termwidth(self):
1491 def termwidth(self):
1483 '''how wide is the terminal in columns?
1492 '''how wide is the terminal in columns?
1484 '''
1493 '''
1485 if b'COLUMNS' in encoding.environ:
1494 if b'COLUMNS' in encoding.environ:
1486 try:
1495 try:
1487 return int(encoding.environ[b'COLUMNS'])
1496 return int(encoding.environ[b'COLUMNS'])
1488 except ValueError:
1497 except ValueError:
1489 pass
1498 pass
1490 return scmutil.termsize(self)[0]
1499 return scmutil.termsize(self)[0]
1491
1500
1492 def formatted(self):
1501 def formatted(self):
1493 '''should formatted output be used?
1502 '''should formatted output be used?
1494
1503
1495 It is often desirable to format the output to suite the output medium.
1504 It is often desirable to format the output to suite the output medium.
1496 Examples of this are truncating long lines or colorizing messages.
1505 Examples of this are truncating long lines or colorizing messages.
1497 However, this is not often not desirable when piping output into other
1506 However, this is not often not desirable when piping output into other
1498 utilities, e.g. `grep'.
1507 utilities, e.g. `grep'.
1499
1508
1500 Formatted output is triggered by the value of the `ui.formatted'
1509 Formatted output is triggered by the value of the `ui.formatted'
1501 configuration variable or - if it is unset - when `sys.stdout' points
1510 configuration variable or - if it is unset - when `sys.stdout' points
1502 to a terminal device. Please note that `ui.formatted' should be
1511 to a terminal device. Please note that `ui.formatted' should be
1503 considered an implementation detail; it is not intended for use outside
1512 considered an implementation detail; it is not intended for use outside
1504 Mercurial or its extensions.
1513 Mercurial or its extensions.
1505
1514
1506 This function refers to output only; for input, see `ui.interactive()'.
1515 This function refers to output only; for input, see `ui.interactive()'.
1507 This function always returns false when in plain mode, see `ui.plain()'.
1516 This function always returns false when in plain mode, see `ui.plain()'.
1508 '''
1517 '''
1509 if self.plain():
1518 if self.plain():
1510 return False
1519 return False
1511
1520
1512 i = self.configbool(b"ui", b"formatted")
1521 i = self.configbool(b"ui", b"formatted")
1513 if i is None:
1522 if i is None:
1514 # some environments replace stdout without implementing isatty
1523 # some environments replace stdout without implementing isatty
1515 # usually those are non-interactive
1524 # usually those are non-interactive
1516 return self._isatty(self._fout)
1525 return self._isatty(self._fout)
1517
1526
1518 return i
1527 return i
1519
1528
1520 def _readline(self, prompt=b' ', promptopts=None):
1529 def _readline(self, prompt=b' ', promptopts=None):
1521 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1530 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1522 # because they have to be text streams with *no buffering*. Instead,
1531 # because they have to be text streams with *no buffering*. Instead,
1523 # we use rawinput() only if call_readline() will be invoked by
1532 # we use rawinput() only if call_readline() will be invoked by
1524 # PyOS_Readline(), so no I/O will be made at Python layer.
1533 # PyOS_Readline(), so no I/O will be made at Python layer.
1525 usereadline = (
1534 usereadline = (
1526 self._isatty(self._fin)
1535 self._isatty(self._fin)
1527 and self._isatty(self._fout)
1536 and self._isatty(self._fout)
1528 and procutil.isstdin(self._fin)
1537 and procutil.isstdin(self._fin)
1529 and procutil.isstdout(self._fout)
1538 and procutil.isstdout(self._fout)
1530 )
1539 )
1531 if usereadline:
1540 if usereadline:
1532 try:
1541 try:
1533 # magically add command line editing support, where
1542 # magically add command line editing support, where
1534 # available
1543 # available
1535 import readline
1544 import readline
1536
1545
1537 # force demandimport to really load the module
1546 # force demandimport to really load the module
1538 readline.read_history_file
1547 readline.read_history_file
1539 # windows sometimes raises something other than ImportError
1548 # windows sometimes raises something other than ImportError
1540 except Exception:
1549 except Exception:
1541 usereadline = False
1550 usereadline = False
1542
1551
1543 if self._colormode == b'win32' or not usereadline:
1552 if self._colormode == b'win32' or not usereadline:
1544 if not promptopts:
1553 if not promptopts:
1545 promptopts = {}
1554 promptopts = {}
1546 self._writemsgnobuf(
1555 self._writemsgnobuf(
1547 self._fmsgout, prompt, type=b'prompt', **promptopts
1556 self._fmsgout, prompt, type=b'prompt', **promptopts
1548 )
1557 )
1549 self.flush()
1558 self.flush()
1550 prompt = b' '
1559 prompt = b' '
1551 else:
1560 else:
1552 prompt = self.label(prompt, b'ui.prompt') + b' '
1561 prompt = self.label(prompt, b'ui.prompt') + b' '
1553
1562
1554 # prompt ' ' must exist; otherwise readline may delete entire line
1563 # prompt ' ' must exist; otherwise readline may delete entire line
1555 # - http://bugs.python.org/issue12833
1564 # - http://bugs.python.org/issue12833
1556 with self.timeblockedsection(b'stdio'):
1565 with self.timeblockedsection(b'stdio'):
1557 if usereadline:
1566 if usereadline:
1558 prompt = encoding.strfromlocal(prompt)
1567 prompt = encoding.strfromlocal(prompt)
1559 line = encoding.strtolocal(pycompat.rawinput(prompt))
1568 line = encoding.strtolocal(pycompat.rawinput(prompt))
1560 # When stdin is in binary mode on Windows, it can cause
1569 # When stdin is in binary mode on Windows, it can cause
1561 # raw_input() to emit an extra trailing carriage return
1570 # raw_input() to emit an extra trailing carriage return
1562 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1571 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1563 line = line[:-1]
1572 line = line[:-1]
1564 else:
1573 else:
1565 self._fout.write(pycompat.bytestr(prompt))
1574 self._fout.write(pycompat.bytestr(prompt))
1566 self._fout.flush()
1575 self._fout.flush()
1567 line = self._fin.readline()
1576 line = self._fin.readline()
1568 if not line:
1577 if not line:
1569 raise EOFError
1578 raise EOFError
1570 line = line.rstrip(pycompat.oslinesep)
1579 line = line.rstrip(pycompat.oslinesep)
1571
1580
1572 return line
1581 return line
1573
1582
1574 def prompt(self, msg, default=b"y"):
1583 def prompt(self, msg, default=b"y"):
1575 """Prompt user with msg, read response.
1584 """Prompt user with msg, read response.
1576 If ui is not interactive, the default is returned.
1585 If ui is not interactive, the default is returned.
1577 """
1586 """
1578 return self._prompt(msg, default=default)
1587 return self._prompt(msg, default=default)
1579
1588
1580 def _prompt(self, msg, **opts):
1589 def _prompt(self, msg, **opts):
1581 default = opts[r'default']
1590 default = opts[r'default']
1582 if not self.interactive():
1591 if not self.interactive():
1583 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1592 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1584 self._writemsg(
1593 self._writemsg(
1585 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1594 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1586 )
1595 )
1587 return default
1596 return default
1588 try:
1597 try:
1589 r = self._readline(prompt=msg, promptopts=opts)
1598 r = self._readline(prompt=msg, promptopts=opts)
1590 if not r:
1599 if not r:
1591 r = default
1600 r = default
1592 if self.configbool(b'ui', b'promptecho'):
1601 if self.configbool(b'ui', b'promptecho'):
1593 self._writemsg(self._fmsgout, r, b"\n", type=b'promptecho')
1602 self._writemsg(self._fmsgout, r, b"\n", type=b'promptecho')
1594 return r
1603 return r
1595 except EOFError:
1604 except EOFError:
1596 raise error.ResponseExpected()
1605 raise error.ResponseExpected()
1597
1606
1598 @staticmethod
1607 @staticmethod
1599 def extractchoices(prompt):
1608 def extractchoices(prompt):
1600 """Extract prompt message and list of choices from specified prompt.
1609 """Extract prompt message and list of choices from specified prompt.
1601
1610
1602 This returns tuple "(message, choices)", and "choices" is the
1611 This returns tuple "(message, choices)", and "choices" is the
1603 list of tuple "(response character, text without &)".
1612 list of tuple "(response character, text without &)".
1604
1613
1605 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1614 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1606 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1615 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1607 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1616 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1608 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1617 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1609 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1618 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1610 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1619 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1611 """
1620 """
1612
1621
1613 # Sadly, the prompt string may have been built with a filename
1622 # Sadly, the prompt string may have been built with a filename
1614 # containing "$$" so let's try to find the first valid-looking
1623 # containing "$$" so let's try to find the first valid-looking
1615 # prompt to start parsing. Sadly, we also can't rely on
1624 # prompt to start parsing. Sadly, we also can't rely on
1616 # choices containing spaces, ASCII, or basically anything
1625 # choices containing spaces, ASCII, or basically anything
1617 # except an ampersand followed by a character.
1626 # except an ampersand followed by a character.
1618 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1627 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1619 msg = m.group(1)
1628 msg = m.group(1)
1620 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1629 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1621
1630
1622 def choicetuple(s):
1631 def choicetuple(s):
1623 ampidx = s.index(b'&')
1632 ampidx = s.index(b'&')
1624 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1633 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1625
1634
1626 return (msg, [choicetuple(s) for s in choices])
1635 return (msg, [choicetuple(s) for s in choices])
1627
1636
1628 def promptchoice(self, prompt, default=0):
1637 def promptchoice(self, prompt, default=0):
1629 """Prompt user with a message, read response, and ensure it matches
1638 """Prompt user with a message, read response, and ensure it matches
1630 one of the provided choices. The prompt is formatted as follows:
1639 one of the provided choices. The prompt is formatted as follows:
1631
1640
1632 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1641 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1633
1642
1634 The index of the choice is returned. Responses are case
1643 The index of the choice is returned. Responses are case
1635 insensitive. If ui is not interactive, the default is
1644 insensitive. If ui is not interactive, the default is
1636 returned.
1645 returned.
1637 """
1646 """
1638
1647
1639 msg, choices = self.extractchoices(prompt)
1648 msg, choices = self.extractchoices(prompt)
1640 resps = [r for r, t in choices]
1649 resps = [r for r, t in choices]
1641 while True:
1650 while True:
1642 r = self._prompt(msg, default=resps[default], choices=choices)
1651 r = self._prompt(msg, default=resps[default], choices=choices)
1643 if r.lower() in resps:
1652 if r.lower() in resps:
1644 return resps.index(r.lower())
1653 return resps.index(r.lower())
1645 # TODO: shouldn't it be a warning?
1654 # TODO: shouldn't it be a warning?
1646 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1655 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1647
1656
1648 def getpass(self, prompt=None, default=None):
1657 def getpass(self, prompt=None, default=None):
1649 if not self.interactive():
1658 if not self.interactive():
1650 return default
1659 return default
1651 try:
1660 try:
1652 self._writemsg(
1661 self._writemsg(
1653 self._fmsgerr,
1662 self._fmsgerr,
1654 prompt or _(b'password: '),
1663 prompt or _(b'password: '),
1655 type=b'prompt',
1664 type=b'prompt',
1656 password=True,
1665 password=True,
1657 )
1666 )
1658 # disable getpass() only if explicitly specified. it's still valid
1667 # disable getpass() only if explicitly specified. it's still valid
1659 # to interact with tty even if fin is not a tty.
1668 # to interact with tty even if fin is not a tty.
1660 with self.timeblockedsection(b'stdio'):
1669 with self.timeblockedsection(b'stdio'):
1661 if self.configbool(b'ui', b'nontty'):
1670 if self.configbool(b'ui', b'nontty'):
1662 l = self._fin.readline()
1671 l = self._fin.readline()
1663 if not l:
1672 if not l:
1664 raise EOFError
1673 raise EOFError
1665 return l.rstrip(b'\n')
1674 return l.rstrip(b'\n')
1666 else:
1675 else:
1667 return getpass.getpass(r'')
1676 return getpass.getpass(r'')
1668 except EOFError:
1677 except EOFError:
1669 raise error.ResponseExpected()
1678 raise error.ResponseExpected()
1670
1679
1671 def status(self, *msg, **opts):
1680 def status(self, *msg, **opts):
1672 '''write status message to output (if ui.quiet is False)
1681 '''write status message to output (if ui.quiet is False)
1673
1682
1674 This adds an output label of "ui.status".
1683 This adds an output label of "ui.status".
1675 '''
1684 '''
1676 if not self.quiet:
1685 if not self.quiet:
1677 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1686 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1678
1687
1679 def warn(self, *msg, **opts):
1688 def warn(self, *msg, **opts):
1680 '''write warning message to output (stderr)
1689 '''write warning message to output (stderr)
1681
1690
1682 This adds an output label of "ui.warning".
1691 This adds an output label of "ui.warning".
1683 '''
1692 '''
1684 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1693 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1685
1694
1686 def error(self, *msg, **opts):
1695 def error(self, *msg, **opts):
1687 '''write error message to output (stderr)
1696 '''write error message to output (stderr)
1688
1697
1689 This adds an output label of "ui.error".
1698 This adds an output label of "ui.error".
1690 '''
1699 '''
1691 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1700 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1692
1701
1693 def note(self, *msg, **opts):
1702 def note(self, *msg, **opts):
1694 '''write note to output (if ui.verbose is True)
1703 '''write note to output (if ui.verbose is True)
1695
1704
1696 This adds an output label of "ui.note".
1705 This adds an output label of "ui.note".
1697 '''
1706 '''
1698 if self.verbose:
1707 if self.verbose:
1699 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1708 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1700
1709
1701 def debug(self, *msg, **opts):
1710 def debug(self, *msg, **opts):
1702 '''write debug message to output (if ui.debugflag is True)
1711 '''write debug message to output (if ui.debugflag is True)
1703
1712
1704 This adds an output label of "ui.debug".
1713 This adds an output label of "ui.debug".
1705 '''
1714 '''
1706 if self.debugflag:
1715 if self.debugflag:
1707 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1716 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1708 self.log(b'debug', b'%s', b''.join(msg))
1717 self.log(b'debug', b'%s', b''.join(msg))
1709
1718
1710 # Aliases to defeat check-code.
1719 # Aliases to defeat check-code.
1711 statusnoi18n = status
1720 statusnoi18n = status
1712 notenoi18n = note
1721 notenoi18n = note
1713 warnnoi18n = warn
1722 warnnoi18n = warn
1714 writenoi18n = write
1723 writenoi18n = write
1715
1724
1716 def edit(
1725 def edit(
1717 self,
1726 self,
1718 text,
1727 text,
1719 user,
1728 user,
1720 extra=None,
1729 extra=None,
1721 editform=None,
1730 editform=None,
1722 pending=None,
1731 pending=None,
1723 repopath=None,
1732 repopath=None,
1724 action=None,
1733 action=None,
1725 ):
1734 ):
1726 if action is None:
1735 if action is None:
1727 self.develwarn(
1736 self.develwarn(
1728 b'action is None but will soon be a required '
1737 b'action is None but will soon be a required '
1729 b'parameter to ui.edit()'
1738 b'parameter to ui.edit()'
1730 )
1739 )
1731 extra_defaults = {
1740 extra_defaults = {
1732 b'prefix': b'editor',
1741 b'prefix': b'editor',
1733 b'suffix': b'.txt',
1742 b'suffix': b'.txt',
1734 }
1743 }
1735 if extra is not None:
1744 if extra is not None:
1736 if extra.get(b'suffix') is not None:
1745 if extra.get(b'suffix') is not None:
1737 self.develwarn(
1746 self.develwarn(
1738 b'extra.suffix is not None but will soon be '
1747 b'extra.suffix is not None but will soon be '
1739 b'ignored by ui.edit()'
1748 b'ignored by ui.edit()'
1740 )
1749 )
1741 extra_defaults.update(extra)
1750 extra_defaults.update(extra)
1742 extra = extra_defaults
1751 extra = extra_defaults
1743
1752
1744 if action == b'diff':
1753 if action == b'diff':
1745 suffix = b'.diff'
1754 suffix = b'.diff'
1746 elif action:
1755 elif action:
1747 suffix = b'.%s.hg.txt' % action
1756 suffix = b'.%s.hg.txt' % action
1748 else:
1757 else:
1749 suffix = extra[b'suffix']
1758 suffix = extra[b'suffix']
1750
1759
1751 rdir = None
1760 rdir = None
1752 if self.configbool(b'experimental', b'editortmpinhg'):
1761 if self.configbool(b'experimental', b'editortmpinhg'):
1753 rdir = repopath
1762 rdir = repopath
1754 (fd, name) = pycompat.mkstemp(
1763 (fd, name) = pycompat.mkstemp(
1755 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1764 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1756 )
1765 )
1757 try:
1766 try:
1758 f = os.fdopen(fd, r'wb')
1767 f = os.fdopen(fd, r'wb')
1759 f.write(util.tonativeeol(text))
1768 f.write(util.tonativeeol(text))
1760 f.close()
1769 f.close()
1761
1770
1762 environ = {b'HGUSER': user}
1771 environ = {b'HGUSER': user}
1763 if b'transplant_source' in extra:
1772 if b'transplant_source' in extra:
1764 environ.update(
1773 environ.update(
1765 {b'HGREVISION': hex(extra[b'transplant_source'])}
1774 {b'HGREVISION': hex(extra[b'transplant_source'])}
1766 )
1775 )
1767 for label in (b'intermediate-source', b'source', b'rebase_source'):
1776 for label in (b'intermediate-source', b'source', b'rebase_source'):
1768 if label in extra:
1777 if label in extra:
1769 environ.update({b'HGREVISION': extra[label]})
1778 environ.update({b'HGREVISION': extra[label]})
1770 break
1779 break
1771 if editform:
1780 if editform:
1772 environ.update({b'HGEDITFORM': editform})
1781 environ.update({b'HGEDITFORM': editform})
1773 if pending:
1782 if pending:
1774 environ.update({b'HG_PENDING': pending})
1783 environ.update({b'HG_PENDING': pending})
1775
1784
1776 editor = self.geteditor()
1785 editor = self.geteditor()
1777
1786
1778 self.system(
1787 self.system(
1779 b"%s \"%s\"" % (editor, name),
1788 b"%s \"%s\"" % (editor, name),
1780 environ=environ,
1789 environ=environ,
1781 onerr=error.Abort,
1790 onerr=error.Abort,
1782 errprefix=_(b"edit failed"),
1791 errprefix=_(b"edit failed"),
1783 blockedtag=b'editor',
1792 blockedtag=b'editor',
1784 )
1793 )
1785
1794
1786 f = open(name, r'rb')
1795 f = open(name, r'rb')
1787 t = util.fromnativeeol(f.read())
1796 t = util.fromnativeeol(f.read())
1788 f.close()
1797 f.close()
1789 finally:
1798 finally:
1790 os.unlink(name)
1799 os.unlink(name)
1791
1800
1792 return t
1801 return t
1793
1802
1794 def system(
1803 def system(
1795 self,
1804 self,
1796 cmd,
1805 cmd,
1797 environ=None,
1806 environ=None,
1798 cwd=None,
1807 cwd=None,
1799 onerr=None,
1808 onerr=None,
1800 errprefix=None,
1809 errprefix=None,
1801 blockedtag=None,
1810 blockedtag=None,
1802 ):
1811 ):
1803 '''execute shell command with appropriate output stream. command
1812 '''execute shell command with appropriate output stream. command
1804 output will be redirected if fout is not stdout.
1813 output will be redirected if fout is not stdout.
1805
1814
1806 if command fails and onerr is None, return status, else raise onerr
1815 if command fails and onerr is None, return status, else raise onerr
1807 object as exception.
1816 object as exception.
1808 '''
1817 '''
1809 if blockedtag is None:
1818 if blockedtag is None:
1810 # Long cmds tend to be because of an absolute path on cmd. Keep
1819 # Long cmds tend to be because of an absolute path on cmd. Keep
1811 # the tail end instead
1820 # the tail end instead
1812 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1821 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1813 blockedtag = b'unknown_system_' + cmdsuffix
1822 blockedtag = b'unknown_system_' + cmdsuffix
1814 out = self._fout
1823 out = self._fout
1815 if any(s[1] for s in self._bufferstates):
1824 if any(s[1] for s in self._bufferstates):
1816 out = self
1825 out = self
1817 with self.timeblockedsection(blockedtag):
1826 with self.timeblockedsection(blockedtag):
1818 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1827 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1819 if rc and onerr:
1828 if rc and onerr:
1820 errmsg = b'%s %s' % (
1829 errmsg = b'%s %s' % (
1821 os.path.basename(cmd.split(None, 1)[0]),
1830 os.path.basename(cmd.split(None, 1)[0]),
1822 procutil.explainexit(rc),
1831 procutil.explainexit(rc),
1823 )
1832 )
1824 if errprefix:
1833 if errprefix:
1825 errmsg = b'%s: %s' % (errprefix, errmsg)
1834 errmsg = b'%s: %s' % (errprefix, errmsg)
1826 raise onerr(errmsg)
1835 raise onerr(errmsg)
1827 return rc
1836 return rc
1828
1837
1829 def _runsystem(self, cmd, environ, cwd, out):
1838 def _runsystem(self, cmd, environ, cwd, out):
1830 """actually execute the given shell command (can be overridden by
1839 """actually execute the given shell command (can be overridden by
1831 extensions like chg)"""
1840 extensions like chg)"""
1832 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1841 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1833
1842
1834 def traceback(self, exc=None, force=False):
1843 def traceback(self, exc=None, force=False):
1835 '''print exception traceback if traceback printing enabled or forced.
1844 '''print exception traceback if traceback printing enabled or forced.
1836 only to call in exception handler. returns true if traceback
1845 only to call in exception handler. returns true if traceback
1837 printed.'''
1846 printed.'''
1838 if self.tracebackflag or force:
1847 if self.tracebackflag or force:
1839 if exc is None:
1848 if exc is None:
1840 exc = sys.exc_info()
1849 exc = sys.exc_info()
1841 cause = getattr(exc[1], 'cause', None)
1850 cause = getattr(exc[1], 'cause', None)
1842
1851
1843 if cause is not None:
1852 if cause is not None:
1844 causetb = traceback.format_tb(cause[2])
1853 causetb = traceback.format_tb(cause[2])
1845 exctb = traceback.format_tb(exc[2])
1854 exctb = traceback.format_tb(exc[2])
1846 exconly = traceback.format_exception_only(cause[0], cause[1])
1855 exconly = traceback.format_exception_only(cause[0], cause[1])
1847
1856
1848 # exclude frame where 'exc' was chained and rethrown from exctb
1857 # exclude frame where 'exc' was chained and rethrown from exctb
1849 self.write_err(
1858 self.write_err(
1850 b'Traceback (most recent call last):\n',
1859 b'Traceback (most recent call last):\n',
1851 b''.join(exctb[:-1]),
1860 b''.join(exctb[:-1]),
1852 b''.join(causetb),
1861 b''.join(causetb),
1853 b''.join(exconly),
1862 b''.join(exconly),
1854 )
1863 )
1855 else:
1864 else:
1856 output = traceback.format_exception(exc[0], exc[1], exc[2])
1865 output = traceback.format_exception(exc[0], exc[1], exc[2])
1857 self.write_err(encoding.strtolocal(r''.join(output)))
1866 self.write_err(encoding.strtolocal(r''.join(output)))
1858 return self.tracebackflag or force
1867 return self.tracebackflag or force
1859
1868
1860 def geteditor(self):
1869 def geteditor(self):
1861 '''return editor to use'''
1870 '''return editor to use'''
1862 if pycompat.sysplatform == b'plan9':
1871 if pycompat.sysplatform == b'plan9':
1863 # vi is the MIPS instruction simulator on Plan 9. We
1872 # vi is the MIPS instruction simulator on Plan 9. We
1864 # instead default to E to plumb commit messages to
1873 # instead default to E to plumb commit messages to
1865 # avoid confusion.
1874 # avoid confusion.
1866 editor = b'E'
1875 editor = b'E'
1867 else:
1876 else:
1868 editor = b'vi'
1877 editor = b'vi'
1869 return encoding.environ.get(b"HGEDITOR") or self.config(
1878 return encoding.environ.get(b"HGEDITOR") or self.config(
1870 b"ui", b"editor", editor
1879 b"ui", b"editor", editor
1871 )
1880 )
1872
1881
1873 @util.propertycache
1882 @util.propertycache
1874 def _progbar(self):
1883 def _progbar(self):
1875 """setup the progbar singleton to the ui object"""
1884 """setup the progbar singleton to the ui object"""
1876 if (
1885 if (
1877 self.quiet
1886 self.quiet
1878 or self.debugflag
1887 or self.debugflag
1879 or self.configbool(b'progress', b'disable')
1888 or self.configbool(b'progress', b'disable')
1880 or not progress.shouldprint(self)
1889 or not progress.shouldprint(self)
1881 ):
1890 ):
1882 return None
1891 return None
1883 return getprogbar(self)
1892 return getprogbar(self)
1884
1893
1885 def _progclear(self):
1894 def _progclear(self):
1886 """clear progress bar output if any. use it before any output"""
1895 """clear progress bar output if any. use it before any output"""
1887 if not haveprogbar(): # nothing loaded yet
1896 if not haveprogbar(): # nothing loaded yet
1888 return
1897 return
1889 if self._progbar is not None and self._progbar.printed:
1898 if self._progbar is not None and self._progbar.printed:
1890 self._progbar.clear()
1899 self._progbar.clear()
1891
1900
1892 def progress(self, topic, pos, item=b"", unit=b"", total=None):
1901 def progress(self, topic, pos, item=b"", unit=b"", total=None):
1893 '''show a progress message
1902 '''show a progress message
1894
1903
1895 By default a textual progress bar will be displayed if an operation
1904 By default a textual progress bar will be displayed if an operation
1896 takes too long. 'topic' is the current operation, 'item' is a
1905 takes too long. 'topic' is the current operation, 'item' is a
1897 non-numeric marker of the current position (i.e. the currently
1906 non-numeric marker of the current position (i.e. the currently
1898 in-process file), 'pos' is the current numeric position (i.e.
1907 in-process file), 'pos' is the current numeric position (i.e.
1899 revision, bytes, etc.), unit is a corresponding unit label,
1908 revision, bytes, etc.), unit is a corresponding unit label,
1900 and total is the highest expected pos.
1909 and total is the highest expected pos.
1901
1910
1902 Multiple nested topics may be active at a time.
1911 Multiple nested topics may be active at a time.
1903
1912
1904 All topics should be marked closed by setting pos to None at
1913 All topics should be marked closed by setting pos to None at
1905 termination.
1914 termination.
1906 '''
1915 '''
1907 self.deprecwarn(
1916 self.deprecwarn(
1908 b"use ui.makeprogress() instead of ui.progress()", b"5.1"
1917 b"use ui.makeprogress() instead of ui.progress()", b"5.1"
1909 )
1918 )
1910 progress = self.makeprogress(topic, unit, total)
1919 progress = self.makeprogress(topic, unit, total)
1911 if pos is not None:
1920 if pos is not None:
1912 progress.update(pos, item=item)
1921 progress.update(pos, item=item)
1913 else:
1922 else:
1914 progress.complete()
1923 progress.complete()
1915
1924
1916 def makeprogress(self, topic, unit=b"", total=None):
1925 def makeprogress(self, topic, unit=b"", total=None):
1917 """Create a progress helper for the specified topic"""
1926 """Create a progress helper for the specified topic"""
1918 if getattr(self._fmsgerr, 'structured', False):
1927 if getattr(self._fmsgerr, 'structured', False):
1919 # channel for machine-readable output with metadata, just send
1928 # channel for machine-readable output with metadata, just send
1920 # raw information
1929 # raw information
1921 # TODO: consider porting some useful information (e.g. estimated
1930 # TODO: consider porting some useful information (e.g. estimated
1922 # time) from progbar. we might want to support update delay to
1931 # time) from progbar. we might want to support update delay to
1923 # reduce the cost of transferring progress messages.
1932 # reduce the cost of transferring progress messages.
1924 def updatebar(topic, pos, item, unit, total):
1933 def updatebar(topic, pos, item, unit, total):
1925 self._fmsgerr.write(
1934 self._fmsgerr.write(
1926 None,
1935 None,
1927 type=b'progress',
1936 type=b'progress',
1928 topic=topic,
1937 topic=topic,
1929 pos=pos,
1938 pos=pos,
1930 item=item,
1939 item=item,
1931 unit=unit,
1940 unit=unit,
1932 total=total,
1941 total=total,
1933 )
1942 )
1934
1943
1935 elif self._progbar is not None:
1944 elif self._progbar is not None:
1936 updatebar = self._progbar.progress
1945 updatebar = self._progbar.progress
1937 else:
1946 else:
1938
1947
1939 def updatebar(topic, pos, item, unit, total):
1948 def updatebar(topic, pos, item, unit, total):
1940 pass
1949 pass
1941
1950
1942 return scmutil.progress(self, updatebar, topic, unit, total)
1951 return scmutil.progress(self, updatebar, topic, unit, total)
1943
1952
1944 def getlogger(self, name):
1953 def getlogger(self, name):
1945 """Returns a logger of the given name; or None if not registered"""
1954 """Returns a logger of the given name; or None if not registered"""
1946 return self._loggers.get(name)
1955 return self._loggers.get(name)
1947
1956
1948 def setlogger(self, name, logger):
1957 def setlogger(self, name, logger):
1949 """Install logger which can be identified later by the given name
1958 """Install logger which can be identified later by the given name
1950
1959
1951 More than one loggers can be registered. Use extension or module
1960 More than one loggers can be registered. Use extension or module
1952 name to uniquely identify the logger instance.
1961 name to uniquely identify the logger instance.
1953 """
1962 """
1954 self._loggers[name] = logger
1963 self._loggers[name] = logger
1955
1964
1956 def log(self, event, msgfmt, *msgargs, **opts):
1965 def log(self, event, msgfmt, *msgargs, **opts):
1957 '''hook for logging facility extensions
1966 '''hook for logging facility extensions
1958
1967
1959 event should be a readily-identifiable subsystem, which will
1968 event should be a readily-identifiable subsystem, which will
1960 allow filtering.
1969 allow filtering.
1961
1970
1962 msgfmt should be a newline-terminated format string to log, and
1971 msgfmt should be a newline-terminated format string to log, and
1963 *msgargs are %-formatted into it.
1972 *msgargs are %-formatted into it.
1964
1973
1965 **opts currently has no defined meanings.
1974 **opts currently has no defined meanings.
1966 '''
1975 '''
1967 if not self._loggers:
1976 if not self._loggers:
1968 return
1977 return
1969 activeloggers = [
1978 activeloggers = [
1970 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
1979 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
1971 ]
1980 ]
1972 if not activeloggers:
1981 if not activeloggers:
1973 return
1982 return
1974 msg = msgfmt % msgargs
1983 msg = msgfmt % msgargs
1975 opts = pycompat.byteskwargs(opts)
1984 opts = pycompat.byteskwargs(opts)
1976 # guard against recursion from e.g. ui.debug()
1985 # guard against recursion from e.g. ui.debug()
1977 registeredloggers = self._loggers
1986 registeredloggers = self._loggers
1978 self._loggers = {}
1987 self._loggers = {}
1979 try:
1988 try:
1980 for logger in activeloggers:
1989 for logger in activeloggers:
1981 logger.log(self, event, msg, opts)
1990 logger.log(self, event, msg, opts)
1982 finally:
1991 finally:
1983 self._loggers = registeredloggers
1992 self._loggers = registeredloggers
1984
1993
1985 def label(self, msg, label):
1994 def label(self, msg, label):
1986 '''style msg based on supplied label
1995 '''style msg based on supplied label
1987
1996
1988 If some color mode is enabled, this will add the necessary control
1997 If some color mode is enabled, this will add the necessary control
1989 characters to apply such color. In addition, 'debug' color mode adds
1998 characters to apply such color. In addition, 'debug' color mode adds
1990 markup showing which label affects a piece of text.
1999 markup showing which label affects a piece of text.
1991
2000
1992 ui.write(s, 'label') is equivalent to
2001 ui.write(s, 'label') is equivalent to
1993 ui.write(ui.label(s, 'label')).
2002 ui.write(ui.label(s, 'label')).
1994 '''
2003 '''
1995 if self._colormode is not None:
2004 if self._colormode is not None:
1996 return color.colorlabel(self, msg, label)
2005 return color.colorlabel(self, msg, label)
1997 return msg
2006 return msg
1998
2007
1999 def develwarn(self, msg, stacklevel=1, config=None):
2008 def develwarn(self, msg, stacklevel=1, config=None):
2000 """issue a developer warning message
2009 """issue a developer warning message
2001
2010
2002 Use 'stacklevel' to report the offender some layers further up in the
2011 Use 'stacklevel' to report the offender some layers further up in the
2003 stack.
2012 stack.
2004 """
2013 """
2005 if not self.configbool(b'devel', b'all-warnings'):
2014 if not self.configbool(b'devel', b'all-warnings'):
2006 if config is None or not self.configbool(b'devel', config):
2015 if config is None or not self.configbool(b'devel', config):
2007 return
2016 return
2008 msg = b'devel-warn: ' + msg
2017 msg = b'devel-warn: ' + msg
2009 stacklevel += 1 # get in develwarn
2018 stacklevel += 1 # get in develwarn
2010 if self.tracebackflag:
2019 if self.tracebackflag:
2011 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2020 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2012 self.log(
2021 self.log(
2013 b'develwarn',
2022 b'develwarn',
2014 b'%s at:\n%s'
2023 b'%s at:\n%s'
2015 % (msg, b''.join(util.getstackframes(stacklevel))),
2024 % (msg, b''.join(util.getstackframes(stacklevel))),
2016 )
2025 )
2017 else:
2026 else:
2018 curframe = inspect.currentframe()
2027 curframe = inspect.currentframe()
2019 calframe = inspect.getouterframes(curframe, 2)
2028 calframe = inspect.getouterframes(curframe, 2)
2020 fname, lineno, fmsg = calframe[stacklevel][1:4]
2029 fname, lineno, fmsg = calframe[stacklevel][1:4]
2021 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2030 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2022 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2031 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2023 self.log(
2032 self.log(
2024 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2033 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2025 )
2034 )
2026 curframe = calframe = None # avoid cycles
2035 curframe = calframe = None # avoid cycles
2027
2036
2028 def deprecwarn(self, msg, version, stacklevel=2):
2037 def deprecwarn(self, msg, version, stacklevel=2):
2029 """issue a deprecation warning
2038 """issue a deprecation warning
2030
2039
2031 - msg: message explaining what is deprecated and how to upgrade,
2040 - msg: message explaining what is deprecated and how to upgrade,
2032 - version: last version where the API will be supported,
2041 - version: last version where the API will be supported,
2033 """
2042 """
2034 if not (
2043 if not (
2035 self.configbool(b'devel', b'all-warnings')
2044 self.configbool(b'devel', b'all-warnings')
2036 or self.configbool(b'devel', b'deprec-warn')
2045 or self.configbool(b'devel', b'deprec-warn')
2037 ):
2046 ):
2038 return
2047 return
2039 msg += (
2048 msg += (
2040 b"\n(compatibility will be dropped after Mercurial-%s,"
2049 b"\n(compatibility will be dropped after Mercurial-%s,"
2041 b" update your code.)"
2050 b" update your code.)"
2042 ) % version
2051 ) % version
2043 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2052 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2044
2053
2045 def exportableenviron(self):
2054 def exportableenviron(self):
2046 """The environment variables that are safe to export, e.g. through
2055 """The environment variables that are safe to export, e.g. through
2047 hgweb.
2056 hgweb.
2048 """
2057 """
2049 return self._exportableenviron
2058 return self._exportableenviron
2050
2059
2051 @contextlib.contextmanager
2060 @contextlib.contextmanager
2052 def configoverride(self, overrides, source=b""):
2061 def configoverride(self, overrides, source=b""):
2053 """Context manager for temporary config overrides
2062 """Context manager for temporary config overrides
2054 `overrides` must be a dict of the following structure:
2063 `overrides` must be a dict of the following structure:
2055 {(section, name) : value}"""
2064 {(section, name) : value}"""
2056 backups = {}
2065 backups = {}
2057 try:
2066 try:
2058 for (section, name), value in overrides.items():
2067 for (section, name), value in overrides.items():
2059 backups[(section, name)] = self.backupconfig(section, name)
2068 backups[(section, name)] = self.backupconfig(section, name)
2060 self.setconfig(section, name, value, source)
2069 self.setconfig(section, name, value, source)
2061 yield
2070 yield
2062 finally:
2071 finally:
2063 for __, backup in backups.items():
2072 for __, backup in backups.items():
2064 self.restoreconfig(backup)
2073 self.restoreconfig(backup)
2065 # just restoring ui.quiet config to the previous value is not enough
2074 # just restoring ui.quiet config to the previous value is not enough
2066 # as it does not update ui.quiet class member
2075 # as it does not update ui.quiet class member
2067 if (b'ui', b'quiet') in overrides:
2076 if (b'ui', b'quiet') in overrides:
2068 self.fixconfig(section=b'ui')
2077 self.fixconfig(section=b'ui')
2069
2078
2070
2079
2071 class paths(dict):
2080 class paths(dict):
2072 """Represents a collection of paths and their configs.
2081 """Represents a collection of paths and their configs.
2073
2082
2074 Data is initially derived from ui instances and the config files they have
2083 Data is initially derived from ui instances and the config files they have
2075 loaded.
2084 loaded.
2076 """
2085 """
2077
2086
2078 def __init__(self, ui):
2087 def __init__(self, ui):
2079 dict.__init__(self)
2088 dict.__init__(self)
2080
2089
2081 for name, loc in ui.configitems(b'paths', ignoresub=True):
2090 for name, loc in ui.configitems(b'paths', ignoresub=True):
2082 # No location is the same as not existing.
2091 # No location is the same as not existing.
2083 if not loc:
2092 if not loc:
2084 continue
2093 continue
2085 loc, sub = ui.configsuboptions(b'paths', name)
2094 loc, sub = ui.configsuboptions(b'paths', name)
2086 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2095 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2087
2096
2088 def getpath(self, name, default=None):
2097 def getpath(self, name, default=None):
2089 """Return a ``path`` from a string, falling back to default.
2098 """Return a ``path`` from a string, falling back to default.
2090
2099
2091 ``name`` can be a named path or locations. Locations are filesystem
2100 ``name`` can be a named path or locations. Locations are filesystem
2092 paths or URIs.
2101 paths or URIs.
2093
2102
2094 Returns None if ``name`` is not a registered path, a URI, or a local
2103 Returns None if ``name`` is not a registered path, a URI, or a local
2095 path to a repo.
2104 path to a repo.
2096 """
2105 """
2097 # Only fall back to default if no path was requested.
2106 # Only fall back to default if no path was requested.
2098 if name is None:
2107 if name is None:
2099 if not default:
2108 if not default:
2100 default = ()
2109 default = ()
2101 elif not isinstance(default, (tuple, list)):
2110 elif not isinstance(default, (tuple, list)):
2102 default = (default,)
2111 default = (default,)
2103 for k in default:
2112 for k in default:
2104 try:
2113 try:
2105 return self[k]
2114 return self[k]
2106 except KeyError:
2115 except KeyError:
2107 continue
2116 continue
2108 return None
2117 return None
2109
2118
2110 # Most likely empty string.
2119 # Most likely empty string.
2111 # This may need to raise in the future.
2120 # This may need to raise in the future.
2112 if not name:
2121 if not name:
2113 return None
2122 return None
2114
2123
2115 try:
2124 try:
2116 return self[name]
2125 return self[name]
2117 except KeyError:
2126 except KeyError:
2118 # Try to resolve as a local path or URI.
2127 # Try to resolve as a local path or URI.
2119 try:
2128 try:
2120 # We don't pass sub-options in, so no need to pass ui instance.
2129 # We don't pass sub-options in, so no need to pass ui instance.
2121 return path(None, None, rawloc=name)
2130 return path(None, None, rawloc=name)
2122 except ValueError:
2131 except ValueError:
2123 raise error.RepoError(_(b'repository %s does not exist') % name)
2132 raise error.RepoError(_(b'repository %s does not exist') % name)
2124
2133
2125
2134
2126 _pathsuboptions = {}
2135 _pathsuboptions = {}
2127
2136
2128
2137
2129 def pathsuboption(option, attr):
2138 def pathsuboption(option, attr):
2130 """Decorator used to declare a path sub-option.
2139 """Decorator used to declare a path sub-option.
2131
2140
2132 Arguments are the sub-option name and the attribute it should set on
2141 Arguments are the sub-option name and the attribute it should set on
2133 ``path`` instances.
2142 ``path`` instances.
2134
2143
2135 The decorated function will receive as arguments a ``ui`` instance,
2144 The decorated function will receive as arguments a ``ui`` instance,
2136 ``path`` instance, and the string value of this option from the config.
2145 ``path`` instance, and the string value of this option from the config.
2137 The function should return the value that will be set on the ``path``
2146 The function should return the value that will be set on the ``path``
2138 instance.
2147 instance.
2139
2148
2140 This decorator can be used to perform additional verification of
2149 This decorator can be used to perform additional verification of
2141 sub-options and to change the type of sub-options.
2150 sub-options and to change the type of sub-options.
2142 """
2151 """
2143
2152
2144 def register(func):
2153 def register(func):
2145 _pathsuboptions[option] = (attr, func)
2154 _pathsuboptions[option] = (attr, func)
2146 return func
2155 return func
2147
2156
2148 return register
2157 return register
2149
2158
2150
2159
2151 @pathsuboption(b'pushurl', b'pushloc')
2160 @pathsuboption(b'pushurl', b'pushloc')
2152 def pushurlpathoption(ui, path, value):
2161 def pushurlpathoption(ui, path, value):
2153 u = util.url(value)
2162 u = util.url(value)
2154 # Actually require a URL.
2163 # Actually require a URL.
2155 if not u.scheme:
2164 if not u.scheme:
2156 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2165 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2157 return None
2166 return None
2158
2167
2159 # Don't support the #foo syntax in the push URL to declare branch to
2168 # Don't support the #foo syntax in the push URL to declare branch to
2160 # push.
2169 # push.
2161 if u.fragment:
2170 if u.fragment:
2162 ui.warn(
2171 ui.warn(
2163 _(
2172 _(
2164 b'("#fragment" in paths.%s:pushurl not supported; '
2173 b'("#fragment" in paths.%s:pushurl not supported; '
2165 b'ignoring)\n'
2174 b'ignoring)\n'
2166 )
2175 )
2167 % path.name
2176 % path.name
2168 )
2177 )
2169 u.fragment = None
2178 u.fragment = None
2170
2179
2171 return bytes(u)
2180 return bytes(u)
2172
2181
2173
2182
2174 @pathsuboption(b'pushrev', b'pushrev')
2183 @pathsuboption(b'pushrev', b'pushrev')
2175 def pushrevpathoption(ui, path, value):
2184 def pushrevpathoption(ui, path, value):
2176 return value
2185 return value
2177
2186
2178
2187
2179 class path(object):
2188 class path(object):
2180 """Represents an individual path and its configuration."""
2189 """Represents an individual path and its configuration."""
2181
2190
2182 def __init__(self, ui, name, rawloc=None, suboptions=None):
2191 def __init__(self, ui, name, rawloc=None, suboptions=None):
2183 """Construct a path from its config options.
2192 """Construct a path from its config options.
2184
2193
2185 ``ui`` is the ``ui`` instance the path is coming from.
2194 ``ui`` is the ``ui`` instance the path is coming from.
2186 ``name`` is the symbolic name of the path.
2195 ``name`` is the symbolic name of the path.
2187 ``rawloc`` is the raw location, as defined in the config.
2196 ``rawloc`` is the raw location, as defined in the config.
2188 ``pushloc`` is the raw locations pushes should be made to.
2197 ``pushloc`` is the raw locations pushes should be made to.
2189
2198
2190 If ``name`` is not defined, we require that the location be a) a local
2199 If ``name`` is not defined, we require that the location be a) a local
2191 filesystem path with a .hg directory or b) a URL. If not,
2200 filesystem path with a .hg directory or b) a URL. If not,
2192 ``ValueError`` is raised.
2201 ``ValueError`` is raised.
2193 """
2202 """
2194 if not rawloc:
2203 if not rawloc:
2195 raise ValueError(b'rawloc must be defined')
2204 raise ValueError(b'rawloc must be defined')
2196
2205
2197 # Locations may define branches via syntax <base>#<branch>.
2206 # Locations may define branches via syntax <base>#<branch>.
2198 u = util.url(rawloc)
2207 u = util.url(rawloc)
2199 branch = None
2208 branch = None
2200 if u.fragment:
2209 if u.fragment:
2201 branch = u.fragment
2210 branch = u.fragment
2202 u.fragment = None
2211 u.fragment = None
2203
2212
2204 self.url = u
2213 self.url = u
2205 self.branch = branch
2214 self.branch = branch
2206
2215
2207 self.name = name
2216 self.name = name
2208 self.rawloc = rawloc
2217 self.rawloc = rawloc
2209 self.loc = b'%s' % u
2218 self.loc = b'%s' % u
2210
2219
2211 # When given a raw location but not a symbolic name, validate the
2220 # When given a raw location but not a symbolic name, validate the
2212 # location is valid.
2221 # location is valid.
2213 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2222 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2214 raise ValueError(
2223 raise ValueError(
2215 b'location is not a URL or path to a local '
2224 b'location is not a URL or path to a local '
2216 b'repo: %s' % rawloc
2225 b'repo: %s' % rawloc
2217 )
2226 )
2218
2227
2219 suboptions = suboptions or {}
2228 suboptions = suboptions or {}
2220
2229
2221 # Now process the sub-options. If a sub-option is registered, its
2230 # Now process the sub-options. If a sub-option is registered, its
2222 # attribute will always be present. The value will be None if there
2231 # attribute will always be present. The value will be None if there
2223 # was no valid sub-option.
2232 # was no valid sub-option.
2224 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2233 for suboption, (attr, func) in pycompat.iteritems(_pathsuboptions):
2225 if suboption not in suboptions:
2234 if suboption not in suboptions:
2226 setattr(self, attr, None)
2235 setattr(self, attr, None)
2227 continue
2236 continue
2228
2237
2229 value = func(ui, self, suboptions[suboption])
2238 value = func(ui, self, suboptions[suboption])
2230 setattr(self, attr, value)
2239 setattr(self, attr, value)
2231
2240
2232 def _isvalidlocalpath(self, path):
2241 def _isvalidlocalpath(self, path):
2233 """Returns True if the given path is a potentially valid repository.
2242 """Returns True if the given path is a potentially valid repository.
2234 This is its own function so that extensions can change the definition of
2243 This is its own function so that extensions can change the definition of
2235 'valid' in this case (like when pulling from a git repo into a hg
2244 'valid' in this case (like when pulling from a git repo into a hg
2236 one)."""
2245 one)."""
2237 try:
2246 try:
2238 return os.path.isdir(os.path.join(path, b'.hg'))
2247 return os.path.isdir(os.path.join(path, b'.hg'))
2239 # Python 2 may return TypeError. Python 3, ValueError.
2248 # Python 2 may return TypeError. Python 3, ValueError.
2240 except (TypeError, ValueError):
2249 except (TypeError, ValueError):
2241 return False
2250 return False
2242
2251
2243 @property
2252 @property
2244 def suboptions(self):
2253 def suboptions(self):
2245 """Return sub-options and their values for this path.
2254 """Return sub-options and their values for this path.
2246
2255
2247 This is intended to be used for presentation purposes.
2256 This is intended to be used for presentation purposes.
2248 """
2257 """
2249 d = {}
2258 d = {}
2250 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2259 for subopt, (attr, _func) in pycompat.iteritems(_pathsuboptions):
2251 value = getattr(self, attr)
2260 value = getattr(self, attr)
2252 if value is not None:
2261 if value is not None:
2253 d[subopt] = value
2262 d[subopt] = value
2254 return d
2263 return d
2255
2264
2256
2265
2257 # we instantiate one globally shared progress bar to avoid
2266 # we instantiate one globally shared progress bar to avoid
2258 # competing progress bars when multiple UI objects get created
2267 # competing progress bars when multiple UI objects get created
2259 _progresssingleton = None
2268 _progresssingleton = None
2260
2269
2261
2270
2262 def getprogbar(ui):
2271 def getprogbar(ui):
2263 global _progresssingleton
2272 global _progresssingleton
2264 if _progresssingleton is None:
2273 if _progresssingleton is None:
2265 # passing 'ui' object to the singleton is fishy,
2274 # passing 'ui' object to the singleton is fishy,
2266 # this is how the extension used to work but feel free to rework it.
2275 # this is how the extension used to work but feel free to rework it.
2267 _progresssingleton = progress.progbar(ui)
2276 _progresssingleton = progress.progbar(ui)
2268 return _progresssingleton
2277 return _progresssingleton
2269
2278
2270
2279
2271 def haveprogbar():
2280 def haveprogbar():
2272 return _progresssingleton is not None
2281 return _progresssingleton is not None
2273
2282
2274
2283
2275 def _selectmsgdests(ui):
2284 def _selectmsgdests(ui):
2276 name = ui.config(b'ui', b'message-output')
2285 name = ui.config(b'ui', b'message-output')
2277 if name == b'channel':
2286 if name == b'channel':
2278 if ui.fmsg:
2287 if ui.fmsg:
2279 return ui.fmsg, ui.fmsg
2288 return ui.fmsg, ui.fmsg
2280 else:
2289 else:
2281 # fall back to ferr if channel isn't ready so that status/error
2290 # fall back to ferr if channel isn't ready so that status/error
2282 # messages can be printed
2291 # messages can be printed
2283 return ui.ferr, ui.ferr
2292 return ui.ferr, ui.ferr
2284 if name == b'stdio':
2293 if name == b'stdio':
2285 return ui.fout, ui.ferr
2294 return ui.fout, ui.ferr
2286 if name == b'stderr':
2295 if name == b'stderr':
2287 return ui.ferr, ui.ferr
2296 return ui.ferr, ui.ferr
2288 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2297 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2289
2298
2290
2299
2291 def _writemsgwith(write, dest, *args, **opts):
2300 def _writemsgwith(write, dest, *args, **opts):
2292 """Write ui message with the given ui._write*() function
2301 """Write ui message with the given ui._write*() function
2293
2302
2294 The specified message type is translated to 'ui.<type>' label if the dest
2303 The specified message type is translated to 'ui.<type>' label if the dest
2295 isn't a structured channel, so that the message will be colorized.
2304 isn't a structured channel, so that the message will be colorized.
2296 """
2305 """
2297 # TODO: maybe change 'type' to a mandatory option
2306 # TODO: maybe change 'type' to a mandatory option
2298 if r'type' in opts and not getattr(dest, 'structured', False):
2307 if r'type' in opts and not getattr(dest, 'structured', False):
2299 opts[r'label'] = opts.get(r'label', b'') + b' ui.%s' % opts.pop(r'type')
2308 opts[r'label'] = opts.get(r'label', b'') + b' ui.%s' % opts.pop(r'type')
2300 write(dest, *args, **opts)
2309 write(dest, *args, **opts)
General Comments 0
You need to be logged in to leave comments. Login now