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