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