##// END OF EJS Templates
config: give a more detailed sample repo config...
Jordi Gutiérrez Hermoso -
r22836:5a831e4e default
parent child Browse files
Show More
@@ -1,894 +1,906
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 i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error, formatter
10 import config, scmutil, util, error, formatter
11 from node import hex
11 from node import hex
12
12
13 samplehgrcs = {
13 samplehgrcs = {
14 'user':
14 'user':
15 """# example user config (see "hg help config" for more info)
15 """# example user config (see "hg help config" for more info)
16 [ui]
16 [ui]
17 # name and email, e.g.
17 # name and email, e.g.
18 # username = Jane Doe <jdoe@example.com>
18 # username = Jane Doe <jdoe@example.com>
19 username =
19 username =
20
20
21 [extensions]
21 [extensions]
22 # uncomment these lines to enable some popular extensions
22 # uncomment these lines to enable some popular extensions
23 # (see "hg help extensions" for more info)
23 # (see "hg help extensions" for more info)
24 #
24 #
25 # pager =
25 # pager =
26 # progress =
26 # progress =
27 # color =""",
27 # color =""",
28
28
29 'local':
29 'local':
30 """# example repository config (see "hg help config" for more info)
30 """# example repository config (see "hg help config" for more info)
31 [paths]
32 # path aliases to other clones of this repo in URLs or filesystem paths
33 # (see "hg help config.paths" for more info)
34 #
35 # default = http://example.com/hg/example-repo
36 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
37 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-clone = /home/jdoe/jdoes-clone
39
40 [ui]
41 # name and email (local to this repository, optional), e.g.
42 # username = Jane Doe <jdoe@example.com>
31 """,
43 """,
32
44
33 'global':
45 'global':
34 """# example system-wide hg config (see "hg help config" for more info)
46 """# example system-wide hg config (see "hg help config" for more info)
35
47
36 [extensions]
48 [extensions]
37 # uncomment these lines to enable some popular extensions
49 # uncomment these lines to enable some popular extensions
38 # (see "hg help extensions" for more info)
50 # (see "hg help extensions" for more info)
39 #
51 #
40 # blackbox =
52 # blackbox =
41 # progress =
53 # progress =
42 # color =
54 # color =
43 # pager =""",
55 # pager =""",
44 }
56 }
45
57
46 class ui(object):
58 class ui(object):
47 def __init__(self, src=None):
59 def __init__(self, src=None):
48 # _buffers: used for temporary capture of output
60 # _buffers: used for temporary capture of output
49 self._buffers = []
61 self._buffers = []
50 # _bufferstates: Should the temporary capture includes stderr
62 # _bufferstates: Should the temporary capture includes stderr
51 self._bufferstates = []
63 self._bufferstates = []
52 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
64 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
53 self._reportuntrusted = True
65 self._reportuntrusted = True
54 self._ocfg = config.config() # overlay
66 self._ocfg = config.config() # overlay
55 self._tcfg = config.config() # trusted
67 self._tcfg = config.config() # trusted
56 self._ucfg = config.config() # untrusted
68 self._ucfg = config.config() # untrusted
57 self._trustusers = set()
69 self._trustusers = set()
58 self._trustgroups = set()
70 self._trustgroups = set()
59 self.callhooks = True
71 self.callhooks = True
60
72
61 if src:
73 if src:
62 self.fout = src.fout
74 self.fout = src.fout
63 self.ferr = src.ferr
75 self.ferr = src.ferr
64 self.fin = src.fin
76 self.fin = src.fin
65
77
66 self._tcfg = src._tcfg.copy()
78 self._tcfg = src._tcfg.copy()
67 self._ucfg = src._ucfg.copy()
79 self._ucfg = src._ucfg.copy()
68 self._ocfg = src._ocfg.copy()
80 self._ocfg = src._ocfg.copy()
69 self._trustusers = src._trustusers.copy()
81 self._trustusers = src._trustusers.copy()
70 self._trustgroups = src._trustgroups.copy()
82 self._trustgroups = src._trustgroups.copy()
71 self.environ = src.environ
83 self.environ = src.environ
72 self.callhooks = src.callhooks
84 self.callhooks = src.callhooks
73 self.fixconfig()
85 self.fixconfig()
74 else:
86 else:
75 self.fout = sys.stdout
87 self.fout = sys.stdout
76 self.ferr = sys.stderr
88 self.ferr = sys.stderr
77 self.fin = sys.stdin
89 self.fin = sys.stdin
78
90
79 # shared read-only environment
91 # shared read-only environment
80 self.environ = os.environ
92 self.environ = os.environ
81 # we always trust global config files
93 # we always trust global config files
82 for f in scmutil.rcpath():
94 for f in scmutil.rcpath():
83 self.readconfig(f, trust=True)
95 self.readconfig(f, trust=True)
84
96
85 def copy(self):
97 def copy(self):
86 return self.__class__(self)
98 return self.__class__(self)
87
99
88 def formatter(self, topic, opts):
100 def formatter(self, topic, opts):
89 return formatter.formatter(self, topic, opts)
101 return formatter.formatter(self, topic, opts)
90
102
91 def _trusted(self, fp, f):
103 def _trusted(self, fp, f):
92 st = util.fstat(fp)
104 st = util.fstat(fp)
93 if util.isowner(st):
105 if util.isowner(st):
94 return True
106 return True
95
107
96 tusers, tgroups = self._trustusers, self._trustgroups
108 tusers, tgroups = self._trustusers, self._trustgroups
97 if '*' in tusers or '*' in tgroups:
109 if '*' in tusers or '*' in tgroups:
98 return True
110 return True
99
111
100 user = util.username(st.st_uid)
112 user = util.username(st.st_uid)
101 group = util.groupname(st.st_gid)
113 group = util.groupname(st.st_gid)
102 if user in tusers or group in tgroups or user == util.username():
114 if user in tusers or group in tgroups or user == util.username():
103 return True
115 return True
104
116
105 if self._reportuntrusted:
117 if self._reportuntrusted:
106 self.warn(_('not trusting file %s from untrusted '
118 self.warn(_('not trusting file %s from untrusted '
107 'user %s, group %s\n') % (f, user, group))
119 'user %s, group %s\n') % (f, user, group))
108 return False
120 return False
109
121
110 def readconfig(self, filename, root=None, trust=False,
122 def readconfig(self, filename, root=None, trust=False,
111 sections=None, remap=None):
123 sections=None, remap=None):
112 try:
124 try:
113 fp = open(filename)
125 fp = open(filename)
114 except IOError:
126 except IOError:
115 if not sections: # ignore unless we were looking for something
127 if not sections: # ignore unless we were looking for something
116 return
128 return
117 raise
129 raise
118
130
119 cfg = config.config()
131 cfg = config.config()
120 trusted = sections or trust or self._trusted(fp, filename)
132 trusted = sections or trust or self._trusted(fp, filename)
121
133
122 try:
134 try:
123 cfg.read(filename, fp, sections=sections, remap=remap)
135 cfg.read(filename, fp, sections=sections, remap=remap)
124 fp.close()
136 fp.close()
125 except error.ConfigError, inst:
137 except error.ConfigError, inst:
126 if trusted:
138 if trusted:
127 raise
139 raise
128 self.warn(_("ignored: %s\n") % str(inst))
140 self.warn(_("ignored: %s\n") % str(inst))
129
141
130 if self.plain():
142 if self.plain():
131 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
143 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
132 'logtemplate', 'style',
144 'logtemplate', 'style',
133 'traceback', 'verbose'):
145 'traceback', 'verbose'):
134 if k in cfg['ui']:
146 if k in cfg['ui']:
135 del cfg['ui'][k]
147 del cfg['ui'][k]
136 for k, v in cfg.items('defaults'):
148 for k, v in cfg.items('defaults'):
137 del cfg['defaults'][k]
149 del cfg['defaults'][k]
138 # Don't remove aliases from the configuration if in the exceptionlist
150 # Don't remove aliases from the configuration if in the exceptionlist
139 if self.plain('alias'):
151 if self.plain('alias'):
140 for k, v in cfg.items('alias'):
152 for k, v in cfg.items('alias'):
141 del cfg['alias'][k]
153 del cfg['alias'][k]
142
154
143 if trusted:
155 if trusted:
144 self._tcfg.update(cfg)
156 self._tcfg.update(cfg)
145 self._tcfg.update(self._ocfg)
157 self._tcfg.update(self._ocfg)
146 self._ucfg.update(cfg)
158 self._ucfg.update(cfg)
147 self._ucfg.update(self._ocfg)
159 self._ucfg.update(self._ocfg)
148
160
149 if root is None:
161 if root is None:
150 root = os.path.expanduser('~')
162 root = os.path.expanduser('~')
151 self.fixconfig(root=root)
163 self.fixconfig(root=root)
152
164
153 def fixconfig(self, root=None, section=None):
165 def fixconfig(self, root=None, section=None):
154 if section in (None, 'paths'):
166 if section in (None, 'paths'):
155 # expand vars and ~
167 # expand vars and ~
156 # translate paths relative to root (or home) into absolute paths
168 # translate paths relative to root (or home) into absolute paths
157 root = root or os.getcwd()
169 root = root or os.getcwd()
158 for c in self._tcfg, self._ucfg, self._ocfg:
170 for c in self._tcfg, self._ucfg, self._ocfg:
159 for n, p in c.items('paths'):
171 for n, p in c.items('paths'):
160 if not p:
172 if not p:
161 continue
173 continue
162 if '%%' in p:
174 if '%%' in p:
163 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
175 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
164 % (n, p, self.configsource('paths', n)))
176 % (n, p, self.configsource('paths', n)))
165 p = p.replace('%%', '%')
177 p = p.replace('%%', '%')
166 p = util.expandpath(p)
178 p = util.expandpath(p)
167 if not util.hasscheme(p) and not os.path.isabs(p):
179 if not util.hasscheme(p) and not os.path.isabs(p):
168 p = os.path.normpath(os.path.join(root, p))
180 p = os.path.normpath(os.path.join(root, p))
169 c.set("paths", n, p)
181 c.set("paths", n, p)
170
182
171 if section in (None, 'ui'):
183 if section in (None, 'ui'):
172 # update ui options
184 # update ui options
173 self.debugflag = self.configbool('ui', 'debug')
185 self.debugflag = self.configbool('ui', 'debug')
174 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
186 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
175 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
187 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
176 if self.verbose and self.quiet:
188 if self.verbose and self.quiet:
177 self.quiet = self.verbose = False
189 self.quiet = self.verbose = False
178 self._reportuntrusted = self.debugflag or self.configbool("ui",
190 self._reportuntrusted = self.debugflag or self.configbool("ui",
179 "report_untrusted", True)
191 "report_untrusted", True)
180 self.tracebackflag = self.configbool('ui', 'traceback', False)
192 self.tracebackflag = self.configbool('ui', 'traceback', False)
181
193
182 if section in (None, 'trusted'):
194 if section in (None, 'trusted'):
183 # update trust information
195 # update trust information
184 self._trustusers.update(self.configlist('trusted', 'users'))
196 self._trustusers.update(self.configlist('trusted', 'users'))
185 self._trustgroups.update(self.configlist('trusted', 'groups'))
197 self._trustgroups.update(self.configlist('trusted', 'groups'))
186
198
187 def backupconfig(self, section, item):
199 def backupconfig(self, section, item):
188 return (self._ocfg.backup(section, item),
200 return (self._ocfg.backup(section, item),
189 self._tcfg.backup(section, item),
201 self._tcfg.backup(section, item),
190 self._ucfg.backup(section, item),)
202 self._ucfg.backup(section, item),)
191 def restoreconfig(self, data):
203 def restoreconfig(self, data):
192 self._ocfg.restore(data[0])
204 self._ocfg.restore(data[0])
193 self._tcfg.restore(data[1])
205 self._tcfg.restore(data[1])
194 self._ucfg.restore(data[2])
206 self._ucfg.restore(data[2])
195
207
196 def setconfig(self, section, name, value, source=''):
208 def setconfig(self, section, name, value, source=''):
197 for cfg in (self._ocfg, self._tcfg, self._ucfg):
209 for cfg in (self._ocfg, self._tcfg, self._ucfg):
198 cfg.set(section, name, value, source)
210 cfg.set(section, name, value, source)
199 self.fixconfig(section=section)
211 self.fixconfig(section=section)
200
212
201 def _data(self, untrusted):
213 def _data(self, untrusted):
202 return untrusted and self._ucfg or self._tcfg
214 return untrusted and self._ucfg or self._tcfg
203
215
204 def configsource(self, section, name, untrusted=False):
216 def configsource(self, section, name, untrusted=False):
205 return self._data(untrusted).source(section, name) or 'none'
217 return self._data(untrusted).source(section, name) or 'none'
206
218
207 def config(self, section, name, default=None, untrusted=False):
219 def config(self, section, name, default=None, untrusted=False):
208 if isinstance(name, list):
220 if isinstance(name, list):
209 alternates = name
221 alternates = name
210 else:
222 else:
211 alternates = [name]
223 alternates = [name]
212
224
213 for n in alternates:
225 for n in alternates:
214 value = self._data(untrusted).get(section, n, None)
226 value = self._data(untrusted).get(section, n, None)
215 if value is not None:
227 if value is not None:
216 name = n
228 name = n
217 break
229 break
218 else:
230 else:
219 value = default
231 value = default
220
232
221 if self.debugflag and not untrusted and self._reportuntrusted:
233 if self.debugflag and not untrusted and self._reportuntrusted:
222 for n in alternates:
234 for n in alternates:
223 uvalue = self._ucfg.get(section, n)
235 uvalue = self._ucfg.get(section, n)
224 if uvalue is not None and uvalue != value:
236 if uvalue is not None and uvalue != value:
225 self.debug("ignoring untrusted configuration option "
237 self.debug("ignoring untrusted configuration option "
226 "%s.%s = %s\n" % (section, n, uvalue))
238 "%s.%s = %s\n" % (section, n, uvalue))
227 return value
239 return value
228
240
229 def configpath(self, section, name, default=None, untrusted=False):
241 def configpath(self, section, name, default=None, untrusted=False):
230 'get a path config item, expanded relative to repo root or config file'
242 'get a path config item, expanded relative to repo root or config file'
231 v = self.config(section, name, default, untrusted)
243 v = self.config(section, name, default, untrusted)
232 if v is None:
244 if v is None:
233 return None
245 return None
234 if not os.path.isabs(v) or "://" not in v:
246 if not os.path.isabs(v) or "://" not in v:
235 src = self.configsource(section, name, untrusted)
247 src = self.configsource(section, name, untrusted)
236 if ':' in src:
248 if ':' in src:
237 base = os.path.dirname(src.rsplit(':')[0])
249 base = os.path.dirname(src.rsplit(':')[0])
238 v = os.path.join(base, os.path.expanduser(v))
250 v = os.path.join(base, os.path.expanduser(v))
239 return v
251 return v
240
252
241 def configbool(self, section, name, default=False, untrusted=False):
253 def configbool(self, section, name, default=False, untrusted=False):
242 """parse a configuration element as a boolean
254 """parse a configuration element as a boolean
243
255
244 >>> u = ui(); s = 'foo'
256 >>> u = ui(); s = 'foo'
245 >>> u.setconfig(s, 'true', 'yes')
257 >>> u.setconfig(s, 'true', 'yes')
246 >>> u.configbool(s, 'true')
258 >>> u.configbool(s, 'true')
247 True
259 True
248 >>> u.setconfig(s, 'false', 'no')
260 >>> u.setconfig(s, 'false', 'no')
249 >>> u.configbool(s, 'false')
261 >>> u.configbool(s, 'false')
250 False
262 False
251 >>> u.configbool(s, 'unknown')
263 >>> u.configbool(s, 'unknown')
252 False
264 False
253 >>> u.configbool(s, 'unknown', True)
265 >>> u.configbool(s, 'unknown', True)
254 True
266 True
255 >>> u.setconfig(s, 'invalid', 'somevalue')
267 >>> u.setconfig(s, 'invalid', 'somevalue')
256 >>> u.configbool(s, 'invalid')
268 >>> u.configbool(s, 'invalid')
257 Traceback (most recent call last):
269 Traceback (most recent call last):
258 ...
270 ...
259 ConfigError: foo.invalid is not a boolean ('somevalue')
271 ConfigError: foo.invalid is not a boolean ('somevalue')
260 """
272 """
261
273
262 v = self.config(section, name, None, untrusted)
274 v = self.config(section, name, None, untrusted)
263 if v is None:
275 if v is None:
264 return default
276 return default
265 if isinstance(v, bool):
277 if isinstance(v, bool):
266 return v
278 return v
267 b = util.parsebool(v)
279 b = util.parsebool(v)
268 if b is None:
280 if b is None:
269 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
281 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
270 % (section, name, v))
282 % (section, name, v))
271 return b
283 return b
272
284
273 def configint(self, section, name, default=None, untrusted=False):
285 def configint(self, section, name, default=None, untrusted=False):
274 """parse a configuration element as an integer
286 """parse a configuration element as an integer
275
287
276 >>> u = ui(); s = 'foo'
288 >>> u = ui(); s = 'foo'
277 >>> u.setconfig(s, 'int1', '42')
289 >>> u.setconfig(s, 'int1', '42')
278 >>> u.configint(s, 'int1')
290 >>> u.configint(s, 'int1')
279 42
291 42
280 >>> u.setconfig(s, 'int2', '-42')
292 >>> u.setconfig(s, 'int2', '-42')
281 >>> u.configint(s, 'int2')
293 >>> u.configint(s, 'int2')
282 -42
294 -42
283 >>> u.configint(s, 'unknown', 7)
295 >>> u.configint(s, 'unknown', 7)
284 7
296 7
285 >>> u.setconfig(s, 'invalid', 'somevalue')
297 >>> u.setconfig(s, 'invalid', 'somevalue')
286 >>> u.configint(s, 'invalid')
298 >>> u.configint(s, 'invalid')
287 Traceback (most recent call last):
299 Traceback (most recent call last):
288 ...
300 ...
289 ConfigError: foo.invalid is not an integer ('somevalue')
301 ConfigError: foo.invalid is not an integer ('somevalue')
290 """
302 """
291
303
292 v = self.config(section, name, None, untrusted)
304 v = self.config(section, name, None, untrusted)
293 if v is None:
305 if v is None:
294 return default
306 return default
295 try:
307 try:
296 return int(v)
308 return int(v)
297 except ValueError:
309 except ValueError:
298 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
310 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
299 % (section, name, v))
311 % (section, name, v))
300
312
301 def configbytes(self, section, name, default=0, untrusted=False):
313 def configbytes(self, section, name, default=0, untrusted=False):
302 """parse a configuration element as a quantity in bytes
314 """parse a configuration element as a quantity in bytes
303
315
304 Units can be specified as b (bytes), k or kb (kilobytes), m or
316 Units can be specified as b (bytes), k or kb (kilobytes), m or
305 mb (megabytes), g or gb (gigabytes).
317 mb (megabytes), g or gb (gigabytes).
306
318
307 >>> u = ui(); s = 'foo'
319 >>> u = ui(); s = 'foo'
308 >>> u.setconfig(s, 'val1', '42')
320 >>> u.setconfig(s, 'val1', '42')
309 >>> u.configbytes(s, 'val1')
321 >>> u.configbytes(s, 'val1')
310 42
322 42
311 >>> u.setconfig(s, 'val2', '42.5 kb')
323 >>> u.setconfig(s, 'val2', '42.5 kb')
312 >>> u.configbytes(s, 'val2')
324 >>> u.configbytes(s, 'val2')
313 43520
325 43520
314 >>> u.configbytes(s, 'unknown', '7 MB')
326 >>> u.configbytes(s, 'unknown', '7 MB')
315 7340032
327 7340032
316 >>> u.setconfig(s, 'invalid', 'somevalue')
328 >>> u.setconfig(s, 'invalid', 'somevalue')
317 >>> u.configbytes(s, 'invalid')
329 >>> u.configbytes(s, 'invalid')
318 Traceback (most recent call last):
330 Traceback (most recent call last):
319 ...
331 ...
320 ConfigError: foo.invalid is not a byte quantity ('somevalue')
332 ConfigError: foo.invalid is not a byte quantity ('somevalue')
321 """
333 """
322
334
323 value = self.config(section, name)
335 value = self.config(section, name)
324 if value is None:
336 if value is None:
325 if not isinstance(default, str):
337 if not isinstance(default, str):
326 return default
338 return default
327 value = default
339 value = default
328 try:
340 try:
329 return util.sizetoint(value)
341 return util.sizetoint(value)
330 except error.ParseError:
342 except error.ParseError:
331 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
343 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
332 % (section, name, value))
344 % (section, name, value))
333
345
334 def configlist(self, section, name, default=None, untrusted=False):
346 def configlist(self, section, name, default=None, untrusted=False):
335 """parse a configuration element as a list of comma/space separated
347 """parse a configuration element as a list of comma/space separated
336 strings
348 strings
337
349
338 >>> u = ui(); s = 'foo'
350 >>> u = ui(); s = 'foo'
339 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
351 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
340 >>> u.configlist(s, 'list1')
352 >>> u.configlist(s, 'list1')
341 ['this', 'is', 'a small', 'test']
353 ['this', 'is', 'a small', 'test']
342 """
354 """
343
355
344 def _parse_plain(parts, s, offset):
356 def _parse_plain(parts, s, offset):
345 whitespace = False
357 whitespace = False
346 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
358 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
347 whitespace = True
359 whitespace = True
348 offset += 1
360 offset += 1
349 if offset >= len(s):
361 if offset >= len(s):
350 return None, parts, offset
362 return None, parts, offset
351 if whitespace:
363 if whitespace:
352 parts.append('')
364 parts.append('')
353 if s[offset] == '"' and not parts[-1]:
365 if s[offset] == '"' and not parts[-1]:
354 return _parse_quote, parts, offset + 1
366 return _parse_quote, parts, offset + 1
355 elif s[offset] == '"' and parts[-1][-1] == '\\':
367 elif s[offset] == '"' and parts[-1][-1] == '\\':
356 parts[-1] = parts[-1][:-1] + s[offset]
368 parts[-1] = parts[-1][:-1] + s[offset]
357 return _parse_plain, parts, offset + 1
369 return _parse_plain, parts, offset + 1
358 parts[-1] += s[offset]
370 parts[-1] += s[offset]
359 return _parse_plain, parts, offset + 1
371 return _parse_plain, parts, offset + 1
360
372
361 def _parse_quote(parts, s, offset):
373 def _parse_quote(parts, s, offset):
362 if offset < len(s) and s[offset] == '"': # ""
374 if offset < len(s) and s[offset] == '"': # ""
363 parts.append('')
375 parts.append('')
364 offset += 1
376 offset += 1
365 while offset < len(s) and (s[offset].isspace() or
377 while offset < len(s) and (s[offset].isspace() or
366 s[offset] == ','):
378 s[offset] == ','):
367 offset += 1
379 offset += 1
368 return _parse_plain, parts, offset
380 return _parse_plain, parts, offset
369
381
370 while offset < len(s) and s[offset] != '"':
382 while offset < len(s) and s[offset] != '"':
371 if (s[offset] == '\\' and offset + 1 < len(s)
383 if (s[offset] == '\\' and offset + 1 < len(s)
372 and s[offset + 1] == '"'):
384 and s[offset + 1] == '"'):
373 offset += 1
385 offset += 1
374 parts[-1] += '"'
386 parts[-1] += '"'
375 else:
387 else:
376 parts[-1] += s[offset]
388 parts[-1] += s[offset]
377 offset += 1
389 offset += 1
378
390
379 if offset >= len(s):
391 if offset >= len(s):
380 real_parts = _configlist(parts[-1])
392 real_parts = _configlist(parts[-1])
381 if not real_parts:
393 if not real_parts:
382 parts[-1] = '"'
394 parts[-1] = '"'
383 else:
395 else:
384 real_parts[0] = '"' + real_parts[0]
396 real_parts[0] = '"' + real_parts[0]
385 parts = parts[:-1]
397 parts = parts[:-1]
386 parts.extend(real_parts)
398 parts.extend(real_parts)
387 return None, parts, offset
399 return None, parts, offset
388
400
389 offset += 1
401 offset += 1
390 while offset < len(s) and s[offset] in [' ', ',']:
402 while offset < len(s) and s[offset] in [' ', ',']:
391 offset += 1
403 offset += 1
392
404
393 if offset < len(s):
405 if offset < len(s):
394 if offset + 1 == len(s) and s[offset] == '"':
406 if offset + 1 == len(s) and s[offset] == '"':
395 parts[-1] += '"'
407 parts[-1] += '"'
396 offset += 1
408 offset += 1
397 else:
409 else:
398 parts.append('')
410 parts.append('')
399 else:
411 else:
400 return None, parts, offset
412 return None, parts, offset
401
413
402 return _parse_plain, parts, offset
414 return _parse_plain, parts, offset
403
415
404 def _configlist(s):
416 def _configlist(s):
405 s = s.rstrip(' ,')
417 s = s.rstrip(' ,')
406 if not s:
418 if not s:
407 return []
419 return []
408 parser, parts, offset = _parse_plain, [''], 0
420 parser, parts, offset = _parse_plain, [''], 0
409 while parser:
421 while parser:
410 parser, parts, offset = parser(parts, s, offset)
422 parser, parts, offset = parser(parts, s, offset)
411 return parts
423 return parts
412
424
413 result = self.config(section, name, untrusted=untrusted)
425 result = self.config(section, name, untrusted=untrusted)
414 if result is None:
426 if result is None:
415 result = default or []
427 result = default or []
416 if isinstance(result, basestring):
428 if isinstance(result, basestring):
417 result = _configlist(result.lstrip(' ,\n'))
429 result = _configlist(result.lstrip(' ,\n'))
418 if result is None:
430 if result is None:
419 result = default or []
431 result = default or []
420 return result
432 return result
421
433
422 def has_section(self, section, untrusted=False):
434 def has_section(self, section, untrusted=False):
423 '''tell whether section exists in config.'''
435 '''tell whether section exists in config.'''
424 return section in self._data(untrusted)
436 return section in self._data(untrusted)
425
437
426 def configitems(self, section, untrusted=False):
438 def configitems(self, section, untrusted=False):
427 items = self._data(untrusted).items(section)
439 items = self._data(untrusted).items(section)
428 if self.debugflag and not untrusted and self._reportuntrusted:
440 if self.debugflag and not untrusted and self._reportuntrusted:
429 for k, v in self._ucfg.items(section):
441 for k, v in self._ucfg.items(section):
430 if self._tcfg.get(section, k) != v:
442 if self._tcfg.get(section, k) != v:
431 self.debug("ignoring untrusted configuration option "
443 self.debug("ignoring untrusted configuration option "
432 "%s.%s = %s\n" % (section, k, v))
444 "%s.%s = %s\n" % (section, k, v))
433 return items
445 return items
434
446
435 def walkconfig(self, untrusted=False):
447 def walkconfig(self, untrusted=False):
436 cfg = self._data(untrusted)
448 cfg = self._data(untrusted)
437 for section in cfg.sections():
449 for section in cfg.sections():
438 for name, value in self.configitems(section, untrusted):
450 for name, value in self.configitems(section, untrusted):
439 yield section, name, value
451 yield section, name, value
440
452
441 def plain(self, feature=None):
453 def plain(self, feature=None):
442 '''is plain mode active?
454 '''is plain mode active?
443
455
444 Plain mode means that all configuration variables which affect
456 Plain mode means that all configuration variables which affect
445 the behavior and output of Mercurial should be
457 the behavior and output of Mercurial should be
446 ignored. Additionally, the output should be stable,
458 ignored. Additionally, the output should be stable,
447 reproducible and suitable for use in scripts or applications.
459 reproducible and suitable for use in scripts or applications.
448
460
449 The only way to trigger plain mode is by setting either the
461 The only way to trigger plain mode is by setting either the
450 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
462 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
451
463
452 The return value can either be
464 The return value can either be
453 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
465 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
454 - True otherwise
466 - True otherwise
455 '''
467 '''
456 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
468 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
457 return False
469 return False
458 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
470 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
459 if feature and exceptions:
471 if feature and exceptions:
460 return feature not in exceptions
472 return feature not in exceptions
461 return True
473 return True
462
474
463 def username(self):
475 def username(self):
464 """Return default username to be used in commits.
476 """Return default username to be used in commits.
465
477
466 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
478 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
467 and stop searching if one of these is set.
479 and stop searching if one of these is set.
468 If not found and ui.askusername is True, ask the user, else use
480 If not found and ui.askusername is True, ask the user, else use
469 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
481 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
470 """
482 """
471 user = os.environ.get("HGUSER")
483 user = os.environ.get("HGUSER")
472 if user is None:
484 if user is None:
473 user = self.config("ui", ["username", "user"])
485 user = self.config("ui", ["username", "user"])
474 if user is not None:
486 if user is not None:
475 user = os.path.expandvars(user)
487 user = os.path.expandvars(user)
476 if user is None:
488 if user is None:
477 user = os.environ.get("EMAIL")
489 user = os.environ.get("EMAIL")
478 if user is None and self.configbool("ui", "askusername"):
490 if user is None and self.configbool("ui", "askusername"):
479 user = self.prompt(_("enter a commit username:"), default=None)
491 user = self.prompt(_("enter a commit username:"), default=None)
480 if user is None and not self.interactive():
492 if user is None and not self.interactive():
481 try:
493 try:
482 user = '%s@%s' % (util.getuser(), socket.getfqdn())
494 user = '%s@%s' % (util.getuser(), socket.getfqdn())
483 self.warn(_("no username found, using '%s' instead\n") % user)
495 self.warn(_("no username found, using '%s' instead\n") % user)
484 except KeyError:
496 except KeyError:
485 pass
497 pass
486 if not user:
498 if not user:
487 raise util.Abort(_('no username supplied'),
499 raise util.Abort(_('no username supplied'),
488 hint=_('use "hg config --edit" '
500 hint=_('use "hg config --edit" '
489 'to set your username'))
501 'to set your username'))
490 if "\n" in user:
502 if "\n" in user:
491 raise util.Abort(_("username %s contains a newline\n") % repr(user))
503 raise util.Abort(_("username %s contains a newline\n") % repr(user))
492 return user
504 return user
493
505
494 def shortuser(self, user):
506 def shortuser(self, user):
495 """Return a short representation of a user name or email address."""
507 """Return a short representation of a user name or email address."""
496 if not self.verbose:
508 if not self.verbose:
497 user = util.shortuser(user)
509 user = util.shortuser(user)
498 return user
510 return user
499
511
500 def expandpath(self, loc, default=None):
512 def expandpath(self, loc, default=None):
501 """Return repository location relative to cwd or from [paths]"""
513 """Return repository location relative to cwd or from [paths]"""
502 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
514 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
503 return loc
515 return loc
504
516
505 path = self.config('paths', loc)
517 path = self.config('paths', loc)
506 if not path and default is not None:
518 if not path and default is not None:
507 path = self.config('paths', default)
519 path = self.config('paths', default)
508 return path or loc
520 return path or loc
509
521
510 def pushbuffer(self, error=False):
522 def pushbuffer(self, error=False):
511 """install a buffer to capture standar output of the ui object
523 """install a buffer to capture standar output of the ui object
512
524
513 If error is True, the error output will be captured too."""
525 If error is True, the error output will be captured too."""
514 self._buffers.append([])
526 self._buffers.append([])
515 self._bufferstates.append(error)
527 self._bufferstates.append(error)
516
528
517 def popbuffer(self, labeled=False):
529 def popbuffer(self, labeled=False):
518 '''pop the last buffer and return the buffered output
530 '''pop the last buffer and return the buffered output
519
531
520 If labeled is True, any labels associated with buffered
532 If labeled is True, any labels associated with buffered
521 output will be handled. By default, this has no effect
533 output will be handled. By default, this has no effect
522 on the output returned, but extensions and GUI tools may
534 on the output returned, but extensions and GUI tools may
523 handle this argument and returned styled output. If output
535 handle this argument and returned styled output. If output
524 is being buffered so it can be captured and parsed or
536 is being buffered so it can be captured and parsed or
525 processed, labeled should not be set to True.
537 processed, labeled should not be set to True.
526 '''
538 '''
527 self._bufferstates.pop()
539 self._bufferstates.pop()
528 return "".join(self._buffers.pop())
540 return "".join(self._buffers.pop())
529
541
530 def write(self, *args, **opts):
542 def write(self, *args, **opts):
531 '''write args to output
543 '''write args to output
532
544
533 By default, this method simply writes to the buffer or stdout,
545 By default, this method simply writes to the buffer or stdout,
534 but extensions or GUI tools may override this method,
546 but extensions or GUI tools may override this method,
535 write_err(), popbuffer(), and label() to style output from
547 write_err(), popbuffer(), and label() to style output from
536 various parts of hg.
548 various parts of hg.
537
549
538 An optional keyword argument, "label", can be passed in.
550 An optional keyword argument, "label", can be passed in.
539 This should be a string containing label names separated by
551 This should be a string containing label names separated by
540 space. Label names take the form of "topic.type". For example,
552 space. Label names take the form of "topic.type". For example,
541 ui.debug() issues a label of "ui.debug".
553 ui.debug() issues a label of "ui.debug".
542
554
543 When labeling output for a specific command, a label of
555 When labeling output for a specific command, a label of
544 "cmdname.type" is recommended. For example, status issues
556 "cmdname.type" is recommended. For example, status issues
545 a label of "status.modified" for modified files.
557 a label of "status.modified" for modified files.
546 '''
558 '''
547 if self._buffers:
559 if self._buffers:
548 self._buffers[-1].extend([str(a) for a in args])
560 self._buffers[-1].extend([str(a) for a in args])
549 else:
561 else:
550 for a in args:
562 for a in args:
551 self.fout.write(str(a))
563 self.fout.write(str(a))
552
564
553 def write_err(self, *args, **opts):
565 def write_err(self, *args, **opts):
554 try:
566 try:
555 if self._bufferstates and self._bufferstates[-1]:
567 if self._bufferstates and self._bufferstates[-1]:
556 return self.write(*args, **opts)
568 return self.write(*args, **opts)
557 if not getattr(self.fout, 'closed', False):
569 if not getattr(self.fout, 'closed', False):
558 self.fout.flush()
570 self.fout.flush()
559 for a in args:
571 for a in args:
560 self.ferr.write(str(a))
572 self.ferr.write(str(a))
561 # stderr may be buffered under win32 when redirected to files,
573 # stderr may be buffered under win32 when redirected to files,
562 # including stdout.
574 # including stdout.
563 if not getattr(self.ferr, 'closed', False):
575 if not getattr(self.ferr, 'closed', False):
564 self.ferr.flush()
576 self.ferr.flush()
565 except IOError, inst:
577 except IOError, inst:
566 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
578 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
567 raise
579 raise
568
580
569 def flush(self):
581 def flush(self):
570 try: self.fout.flush()
582 try: self.fout.flush()
571 except (IOError, ValueError): pass
583 except (IOError, ValueError): pass
572 try: self.ferr.flush()
584 try: self.ferr.flush()
573 except (IOError, ValueError): pass
585 except (IOError, ValueError): pass
574
586
575 def _isatty(self, fh):
587 def _isatty(self, fh):
576 if self.configbool('ui', 'nontty', False):
588 if self.configbool('ui', 'nontty', False):
577 return False
589 return False
578 return util.isatty(fh)
590 return util.isatty(fh)
579
591
580 def interactive(self):
592 def interactive(self):
581 '''is interactive input allowed?
593 '''is interactive input allowed?
582
594
583 An interactive session is a session where input can be reasonably read
595 An interactive session is a session where input can be reasonably read
584 from `sys.stdin'. If this function returns false, any attempt to read
596 from `sys.stdin'. If this function returns false, any attempt to read
585 from stdin should fail with an error, unless a sensible default has been
597 from stdin should fail with an error, unless a sensible default has been
586 specified.
598 specified.
587
599
588 Interactiveness is triggered by the value of the `ui.interactive'
600 Interactiveness is triggered by the value of the `ui.interactive'
589 configuration variable or - if it is unset - when `sys.stdin' points
601 configuration variable or - if it is unset - when `sys.stdin' points
590 to a terminal device.
602 to a terminal device.
591
603
592 This function refers to input only; for output, see `ui.formatted()'.
604 This function refers to input only; for output, see `ui.formatted()'.
593 '''
605 '''
594 i = self.configbool("ui", "interactive", None)
606 i = self.configbool("ui", "interactive", None)
595 if i is None:
607 if i is None:
596 # some environments replace stdin without implementing isatty
608 # some environments replace stdin without implementing isatty
597 # usually those are non-interactive
609 # usually those are non-interactive
598 return self._isatty(self.fin)
610 return self._isatty(self.fin)
599
611
600 return i
612 return i
601
613
602 def termwidth(self):
614 def termwidth(self):
603 '''how wide is the terminal in columns?
615 '''how wide is the terminal in columns?
604 '''
616 '''
605 if 'COLUMNS' in os.environ:
617 if 'COLUMNS' in os.environ:
606 try:
618 try:
607 return int(os.environ['COLUMNS'])
619 return int(os.environ['COLUMNS'])
608 except ValueError:
620 except ValueError:
609 pass
621 pass
610 return util.termwidth()
622 return util.termwidth()
611
623
612 def formatted(self):
624 def formatted(self):
613 '''should formatted output be used?
625 '''should formatted output be used?
614
626
615 It is often desirable to format the output to suite the output medium.
627 It is often desirable to format the output to suite the output medium.
616 Examples of this are truncating long lines or colorizing messages.
628 Examples of this are truncating long lines or colorizing messages.
617 However, this is not often not desirable when piping output into other
629 However, this is not often not desirable when piping output into other
618 utilities, e.g. `grep'.
630 utilities, e.g. `grep'.
619
631
620 Formatted output is triggered by the value of the `ui.formatted'
632 Formatted output is triggered by the value of the `ui.formatted'
621 configuration variable or - if it is unset - when `sys.stdout' points
633 configuration variable or - if it is unset - when `sys.stdout' points
622 to a terminal device. Please note that `ui.formatted' should be
634 to a terminal device. Please note that `ui.formatted' should be
623 considered an implementation detail; it is not intended for use outside
635 considered an implementation detail; it is not intended for use outside
624 Mercurial or its extensions.
636 Mercurial or its extensions.
625
637
626 This function refers to output only; for input, see `ui.interactive()'.
638 This function refers to output only; for input, see `ui.interactive()'.
627 This function always returns false when in plain mode, see `ui.plain()'.
639 This function always returns false when in plain mode, see `ui.plain()'.
628 '''
640 '''
629 if self.plain():
641 if self.plain():
630 return False
642 return False
631
643
632 i = self.configbool("ui", "formatted", None)
644 i = self.configbool("ui", "formatted", None)
633 if i is None:
645 if i is None:
634 # some environments replace stdout without implementing isatty
646 # some environments replace stdout without implementing isatty
635 # usually those are non-interactive
647 # usually those are non-interactive
636 return self._isatty(self.fout)
648 return self._isatty(self.fout)
637
649
638 return i
650 return i
639
651
640 def _readline(self, prompt=''):
652 def _readline(self, prompt=''):
641 if self._isatty(self.fin):
653 if self._isatty(self.fin):
642 try:
654 try:
643 # magically add command line editing support, where
655 # magically add command line editing support, where
644 # available
656 # available
645 import readline
657 import readline
646 # force demandimport to really load the module
658 # force demandimport to really load the module
647 readline.read_history_file
659 readline.read_history_file
648 # windows sometimes raises something other than ImportError
660 # windows sometimes raises something other than ImportError
649 except Exception:
661 except Exception:
650 pass
662 pass
651
663
652 # call write() so output goes through subclassed implementation
664 # call write() so output goes through subclassed implementation
653 # e.g. color extension on Windows
665 # e.g. color extension on Windows
654 self.write(prompt)
666 self.write(prompt)
655
667
656 # instead of trying to emulate raw_input, swap (self.fin,
668 # instead of trying to emulate raw_input, swap (self.fin,
657 # self.fout) with (sys.stdin, sys.stdout)
669 # self.fout) with (sys.stdin, sys.stdout)
658 oldin = sys.stdin
670 oldin = sys.stdin
659 oldout = sys.stdout
671 oldout = sys.stdout
660 sys.stdin = self.fin
672 sys.stdin = self.fin
661 sys.stdout = self.fout
673 sys.stdout = self.fout
662 # prompt ' ' must exist; otherwise readline may delete entire line
674 # prompt ' ' must exist; otherwise readline may delete entire line
663 # - http://bugs.python.org/issue12833
675 # - http://bugs.python.org/issue12833
664 line = raw_input(' ')
676 line = raw_input(' ')
665 sys.stdin = oldin
677 sys.stdin = oldin
666 sys.stdout = oldout
678 sys.stdout = oldout
667
679
668 # When stdin is in binary mode on Windows, it can cause
680 # When stdin is in binary mode on Windows, it can cause
669 # raw_input() to emit an extra trailing carriage return
681 # raw_input() to emit an extra trailing carriage return
670 if os.linesep == '\r\n' and line and line[-1] == '\r':
682 if os.linesep == '\r\n' and line and line[-1] == '\r':
671 line = line[:-1]
683 line = line[:-1]
672 return line
684 return line
673
685
674 def prompt(self, msg, default="y"):
686 def prompt(self, msg, default="y"):
675 """Prompt user with msg, read response.
687 """Prompt user with msg, read response.
676 If ui is not interactive, the default is returned.
688 If ui is not interactive, the default is returned.
677 """
689 """
678 if not self.interactive():
690 if not self.interactive():
679 self.write(msg, ' ', default, "\n")
691 self.write(msg, ' ', default, "\n")
680 return default
692 return default
681 try:
693 try:
682 r = self._readline(self.label(msg, 'ui.prompt'))
694 r = self._readline(self.label(msg, 'ui.prompt'))
683 if not r:
695 if not r:
684 r = default
696 r = default
685 # sometimes self.interactive disagrees with isatty,
697 # sometimes self.interactive disagrees with isatty,
686 # show response provided on stdin when simulating
698 # show response provided on stdin when simulating
687 # but commandserver
699 # but commandserver
688 if (not util.isatty(self.fin)
700 if (not util.isatty(self.fin)
689 and not self.configbool('ui', 'nontty')):
701 and not self.configbool('ui', 'nontty')):
690 self.write(r, "\n")
702 self.write(r, "\n")
691 return r
703 return r
692 except EOFError:
704 except EOFError:
693 raise util.Abort(_('response expected'))
705 raise util.Abort(_('response expected'))
694
706
695 @staticmethod
707 @staticmethod
696 def extractchoices(prompt):
708 def extractchoices(prompt):
697 """Extract prompt message and list of choices from specified prompt.
709 """Extract prompt message and list of choices from specified prompt.
698
710
699 This returns tuple "(message, choices)", and "choices" is the
711 This returns tuple "(message, choices)", and "choices" is the
700 list of tuple "(response character, text without &)".
712 list of tuple "(response character, text without &)".
701 """
713 """
702 parts = prompt.split('$$')
714 parts = prompt.split('$$')
703 msg = parts[0].rstrip(' ')
715 msg = parts[0].rstrip(' ')
704 choices = [p.strip(' ') for p in parts[1:]]
716 choices = [p.strip(' ') for p in parts[1:]]
705 return (msg,
717 return (msg,
706 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
718 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
707 for s in choices])
719 for s in choices])
708
720
709 def promptchoice(self, prompt, default=0):
721 def promptchoice(self, prompt, default=0):
710 """Prompt user with a message, read response, and ensure it matches
722 """Prompt user with a message, read response, and ensure it matches
711 one of the provided choices. The prompt is formatted as follows:
723 one of the provided choices. The prompt is formatted as follows:
712
724
713 "would you like fries with that (Yn)? $$ &Yes $$ &No"
725 "would you like fries with that (Yn)? $$ &Yes $$ &No"
714
726
715 The index of the choice is returned. Responses are case
727 The index of the choice is returned. Responses are case
716 insensitive. If ui is not interactive, the default is
728 insensitive. If ui is not interactive, the default is
717 returned.
729 returned.
718 """
730 """
719
731
720 msg, choices = self.extractchoices(prompt)
732 msg, choices = self.extractchoices(prompt)
721 resps = [r for r, t in choices]
733 resps = [r for r, t in choices]
722 while True:
734 while True:
723 r = self.prompt(msg, resps[default])
735 r = self.prompt(msg, resps[default])
724 if r.lower() in resps:
736 if r.lower() in resps:
725 return resps.index(r.lower())
737 return resps.index(r.lower())
726 self.write(_("unrecognized response\n"))
738 self.write(_("unrecognized response\n"))
727
739
728 def getpass(self, prompt=None, default=None):
740 def getpass(self, prompt=None, default=None):
729 if not self.interactive():
741 if not self.interactive():
730 return default
742 return default
731 try:
743 try:
732 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
744 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
733 # disable getpass() only if explicitly specified. it's still valid
745 # disable getpass() only if explicitly specified. it's still valid
734 # to interact with tty even if fin is not a tty.
746 # to interact with tty even if fin is not a tty.
735 if self.configbool('ui', 'nontty'):
747 if self.configbool('ui', 'nontty'):
736 return self.fin.readline().rstrip('\n')
748 return self.fin.readline().rstrip('\n')
737 else:
749 else:
738 return getpass.getpass('')
750 return getpass.getpass('')
739 except EOFError:
751 except EOFError:
740 raise util.Abort(_('response expected'))
752 raise util.Abort(_('response expected'))
741 def status(self, *msg, **opts):
753 def status(self, *msg, **opts):
742 '''write status message to output (if ui.quiet is False)
754 '''write status message to output (if ui.quiet is False)
743
755
744 This adds an output label of "ui.status".
756 This adds an output label of "ui.status".
745 '''
757 '''
746 if not self.quiet:
758 if not self.quiet:
747 opts['label'] = opts.get('label', '') + ' ui.status'
759 opts['label'] = opts.get('label', '') + ' ui.status'
748 self.write(*msg, **opts)
760 self.write(*msg, **opts)
749 def warn(self, *msg, **opts):
761 def warn(self, *msg, **opts):
750 '''write warning message to output (stderr)
762 '''write warning message to output (stderr)
751
763
752 This adds an output label of "ui.warning".
764 This adds an output label of "ui.warning".
753 '''
765 '''
754 opts['label'] = opts.get('label', '') + ' ui.warning'
766 opts['label'] = opts.get('label', '') + ' ui.warning'
755 self.write_err(*msg, **opts)
767 self.write_err(*msg, **opts)
756 def note(self, *msg, **opts):
768 def note(self, *msg, **opts):
757 '''write note to output (if ui.verbose is True)
769 '''write note to output (if ui.verbose is True)
758
770
759 This adds an output label of "ui.note".
771 This adds an output label of "ui.note".
760 '''
772 '''
761 if self.verbose:
773 if self.verbose:
762 opts['label'] = opts.get('label', '') + ' ui.note'
774 opts['label'] = opts.get('label', '') + ' ui.note'
763 self.write(*msg, **opts)
775 self.write(*msg, **opts)
764 def debug(self, *msg, **opts):
776 def debug(self, *msg, **opts):
765 '''write debug message to output (if ui.debugflag is True)
777 '''write debug message to output (if ui.debugflag is True)
766
778
767 This adds an output label of "ui.debug".
779 This adds an output label of "ui.debug".
768 '''
780 '''
769 if self.debugflag:
781 if self.debugflag:
770 opts['label'] = opts.get('label', '') + ' ui.debug'
782 opts['label'] = opts.get('label', '') + ' ui.debug'
771 self.write(*msg, **opts)
783 self.write(*msg, **opts)
772 def edit(self, text, user, extra={}, editform=None):
784 def edit(self, text, user, extra={}, editform=None):
773 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
785 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
774 text=True)
786 text=True)
775 try:
787 try:
776 f = os.fdopen(fd, "w")
788 f = os.fdopen(fd, "w")
777 f.write(text)
789 f.write(text)
778 f.close()
790 f.close()
779
791
780 environ = {'HGUSER': user}
792 environ = {'HGUSER': user}
781 if 'transplant_source' in extra:
793 if 'transplant_source' in extra:
782 environ.update({'HGREVISION': hex(extra['transplant_source'])})
794 environ.update({'HGREVISION': hex(extra['transplant_source'])})
783 for label in ('source', 'rebase_source'):
795 for label in ('source', 'rebase_source'):
784 if label in extra:
796 if label in extra:
785 environ.update({'HGREVISION': extra[label]})
797 environ.update({'HGREVISION': extra[label]})
786 break
798 break
787 if editform:
799 if editform:
788 environ.update({'HGEDITFORM': editform})
800 environ.update({'HGEDITFORM': editform})
789
801
790 editor = self.geteditor()
802 editor = self.geteditor()
791
803
792 util.system("%s \"%s\"" % (editor, name),
804 util.system("%s \"%s\"" % (editor, name),
793 environ=environ,
805 environ=environ,
794 onerr=util.Abort, errprefix=_("edit failed"),
806 onerr=util.Abort, errprefix=_("edit failed"),
795 out=self.fout)
807 out=self.fout)
796
808
797 f = open(name)
809 f = open(name)
798 t = f.read()
810 t = f.read()
799 f.close()
811 f.close()
800 finally:
812 finally:
801 os.unlink(name)
813 os.unlink(name)
802
814
803 return t
815 return t
804
816
805 def traceback(self, exc=None, force=False):
817 def traceback(self, exc=None, force=False):
806 '''print exception traceback if traceback printing enabled or forced.
818 '''print exception traceback if traceback printing enabled or forced.
807 only to call in exception handler. returns true if traceback
819 only to call in exception handler. returns true if traceback
808 printed.'''
820 printed.'''
809 if self.tracebackflag or force:
821 if self.tracebackflag or force:
810 if exc is None:
822 if exc is None:
811 exc = sys.exc_info()
823 exc = sys.exc_info()
812 cause = getattr(exc[1], 'cause', None)
824 cause = getattr(exc[1], 'cause', None)
813
825
814 if cause is not None:
826 if cause is not None:
815 causetb = traceback.format_tb(cause[2])
827 causetb = traceback.format_tb(cause[2])
816 exctb = traceback.format_tb(exc[2])
828 exctb = traceback.format_tb(exc[2])
817 exconly = traceback.format_exception_only(cause[0], cause[1])
829 exconly = traceback.format_exception_only(cause[0], cause[1])
818
830
819 # exclude frame where 'exc' was chained and rethrown from exctb
831 # exclude frame where 'exc' was chained and rethrown from exctb
820 self.write_err('Traceback (most recent call last):\n',
832 self.write_err('Traceback (most recent call last):\n',
821 ''.join(exctb[:-1]),
833 ''.join(exctb[:-1]),
822 ''.join(causetb),
834 ''.join(causetb),
823 ''.join(exconly))
835 ''.join(exconly))
824 else:
836 else:
825 traceback.print_exception(exc[0], exc[1], exc[2],
837 traceback.print_exception(exc[0], exc[1], exc[2],
826 file=self.ferr)
838 file=self.ferr)
827 return self.tracebackflag or force
839 return self.tracebackflag or force
828
840
829 def geteditor(self):
841 def geteditor(self):
830 '''return editor to use'''
842 '''return editor to use'''
831 if sys.platform == 'plan9':
843 if sys.platform == 'plan9':
832 # vi is the MIPS instruction simulator on Plan 9. We
844 # vi is the MIPS instruction simulator on Plan 9. We
833 # instead default to E to plumb commit messages to
845 # instead default to E to plumb commit messages to
834 # avoid confusion.
846 # avoid confusion.
835 editor = 'E'
847 editor = 'E'
836 else:
848 else:
837 editor = 'vi'
849 editor = 'vi'
838 return (os.environ.get("HGEDITOR") or
850 return (os.environ.get("HGEDITOR") or
839 self.config("ui", "editor") or
851 self.config("ui", "editor") or
840 os.environ.get("VISUAL") or
852 os.environ.get("VISUAL") or
841 os.environ.get("EDITOR", editor))
853 os.environ.get("EDITOR", editor))
842
854
843 def progress(self, topic, pos, item="", unit="", total=None):
855 def progress(self, topic, pos, item="", unit="", total=None):
844 '''show a progress message
856 '''show a progress message
845
857
846 With stock hg, this is simply a debug message that is hidden
858 With stock hg, this is simply a debug message that is hidden
847 by default, but with extensions or GUI tools it may be
859 by default, but with extensions or GUI tools it may be
848 visible. 'topic' is the current operation, 'item' is a
860 visible. 'topic' is the current operation, 'item' is a
849 non-numeric marker of the current position (i.e. the currently
861 non-numeric marker of the current position (i.e. the currently
850 in-process file), 'pos' is the current numeric position (i.e.
862 in-process file), 'pos' is the current numeric position (i.e.
851 revision, bytes, etc.), unit is a corresponding unit label,
863 revision, bytes, etc.), unit is a corresponding unit label,
852 and total is the highest expected pos.
864 and total is the highest expected pos.
853
865
854 Multiple nested topics may be active at a time.
866 Multiple nested topics may be active at a time.
855
867
856 All topics should be marked closed by setting pos to None at
868 All topics should be marked closed by setting pos to None at
857 termination.
869 termination.
858 '''
870 '''
859
871
860 if pos is None or not self.debugflag:
872 if pos is None or not self.debugflag:
861 return
873 return
862
874
863 if unit:
875 if unit:
864 unit = ' ' + unit
876 unit = ' ' + unit
865 if item:
877 if item:
866 item = ' ' + item
878 item = ' ' + item
867
879
868 if total:
880 if total:
869 pct = 100.0 * pos / total
881 pct = 100.0 * pos / total
870 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
882 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
871 % (topic, item, pos, total, unit, pct))
883 % (topic, item, pos, total, unit, pct))
872 else:
884 else:
873 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
885 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
874
886
875 def log(self, service, *msg, **opts):
887 def log(self, service, *msg, **opts):
876 '''hook for logging facility extensions
888 '''hook for logging facility extensions
877
889
878 service should be a readily-identifiable subsystem, which will
890 service should be a readily-identifiable subsystem, which will
879 allow filtering.
891 allow filtering.
880 message should be a newline-terminated string to log.
892 message should be a newline-terminated string to log.
881 '''
893 '''
882 pass
894 pass
883
895
884 def label(self, msg, label):
896 def label(self, msg, label):
885 '''style msg based on supplied label
897 '''style msg based on supplied label
886
898
887 Like ui.write(), this just returns msg unchanged, but extensions
899 Like ui.write(), this just returns msg unchanged, but extensions
888 and GUI tools can override it to allow styling output without
900 and GUI tools can override it to allow styling output without
889 writing it.
901 writing it.
890
902
891 ui.write(s, 'label') is equivalent to
903 ui.write(s, 'label') is equivalent to
892 ui.write(ui.label(s, 'label')).
904 ui.write(ui.label(s, 'label')).
893 '''
905 '''
894 return msg
906 return msg
General Comments 0
You need to be logged in to leave comments. Login now