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