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