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