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