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