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