##// END OF EJS Templates
ui: represent paths as classes...
Gregory Szorc -
r24250:9c32eea2 default
parent child Browse files
Show More
@@ -1,925 +1,974 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 p = self.paths.getpath(loc, default=default)
535 if not path and default is not None:
535 if p:
536 path = self.config('paths', default)
536 return p.loc
537 return path or loc
537 return loc
538
539 @util.propertycache
540 def paths(self):
541 return paths(self)
538
542
539 def pushbuffer(self, error=False):
543 def pushbuffer(self, error=False):
540 """install a buffer to capture standard output of the ui object
544 """install a buffer to capture standard output of the ui object
541
545
542 If error is True, the error output will be captured too."""
546 If error is True, the error output will be captured too."""
543 self._buffers.append([])
547 self._buffers.append([])
544 self._bufferstates.append(error)
548 self._bufferstates.append(error)
545
549
546 def popbuffer(self, labeled=False):
550 def popbuffer(self, labeled=False):
547 '''pop the last buffer and return the buffered output
551 '''pop the last buffer and return the buffered output
548
552
549 If labeled is True, any labels associated with buffered
553 If labeled is True, any labels associated with buffered
550 output will be handled. By default, this has no effect
554 output will be handled. By default, this has no effect
551 on the output returned, but extensions and GUI tools may
555 on the output returned, but extensions and GUI tools may
552 handle this argument and returned styled output. If output
556 handle this argument and returned styled output. If output
553 is being buffered so it can be captured and parsed or
557 is being buffered so it can be captured and parsed or
554 processed, labeled should not be set to True.
558 processed, labeled should not be set to True.
555 '''
559 '''
556 self._bufferstates.pop()
560 self._bufferstates.pop()
557 return "".join(self._buffers.pop())
561 return "".join(self._buffers.pop())
558
562
559 def write(self, *args, **opts):
563 def write(self, *args, **opts):
560 '''write args to output
564 '''write args to output
561
565
562 By default, this method simply writes to the buffer or stdout,
566 By default, this method simply writes to the buffer or stdout,
563 but extensions or GUI tools may override this method,
567 but extensions or GUI tools may override this method,
564 write_err(), popbuffer(), and label() to style output from
568 write_err(), popbuffer(), and label() to style output from
565 various parts of hg.
569 various parts of hg.
566
570
567 An optional keyword argument, "label", can be passed in.
571 An optional keyword argument, "label", can be passed in.
568 This should be a string containing label names separated by
572 This should be a string containing label names separated by
569 space. Label names take the form of "topic.type". For example,
573 space. Label names take the form of "topic.type". For example,
570 ui.debug() issues a label of "ui.debug".
574 ui.debug() issues a label of "ui.debug".
571
575
572 When labeling output for a specific command, a label of
576 When labeling output for a specific command, a label of
573 "cmdname.type" is recommended. For example, status issues
577 "cmdname.type" is recommended. For example, status issues
574 a label of "status.modified" for modified files.
578 a label of "status.modified" for modified files.
575 '''
579 '''
576 if self._buffers:
580 if self._buffers:
577 self._buffers[-1].extend([str(a) for a in args])
581 self._buffers[-1].extend([str(a) for a in args])
578 else:
582 else:
579 for a in args:
583 for a in args:
580 self.fout.write(str(a))
584 self.fout.write(str(a))
581
585
582 def write_err(self, *args, **opts):
586 def write_err(self, *args, **opts):
583 try:
587 try:
584 if self._bufferstates and self._bufferstates[-1]:
588 if self._bufferstates and self._bufferstates[-1]:
585 return self.write(*args, **opts)
589 return self.write(*args, **opts)
586 if not getattr(self.fout, 'closed', False):
590 if not getattr(self.fout, 'closed', False):
587 self.fout.flush()
591 self.fout.flush()
588 for a in args:
592 for a in args:
589 self.ferr.write(str(a))
593 self.ferr.write(str(a))
590 # stderr may be buffered under win32 when redirected to files,
594 # stderr may be buffered under win32 when redirected to files,
591 # including stdout.
595 # including stdout.
592 if not getattr(self.ferr, 'closed', False):
596 if not getattr(self.ferr, 'closed', False):
593 self.ferr.flush()
597 self.ferr.flush()
594 except IOError, inst:
598 except IOError, inst:
595 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
599 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
596 raise
600 raise
597
601
598 def flush(self):
602 def flush(self):
599 try: self.fout.flush()
603 try: self.fout.flush()
600 except (IOError, ValueError): pass
604 except (IOError, ValueError): pass
601 try: self.ferr.flush()
605 try: self.ferr.flush()
602 except (IOError, ValueError): pass
606 except (IOError, ValueError): pass
603
607
604 def _isatty(self, fh):
608 def _isatty(self, fh):
605 if self.configbool('ui', 'nontty', False):
609 if self.configbool('ui', 'nontty', False):
606 return False
610 return False
607 return util.isatty(fh)
611 return util.isatty(fh)
608
612
609 def interactive(self):
613 def interactive(self):
610 '''is interactive input allowed?
614 '''is interactive input allowed?
611
615
612 An interactive session is a session where input can be reasonably read
616 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
617 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
618 from stdin should fail with an error, unless a sensible default has been
615 specified.
619 specified.
616
620
617 Interactiveness is triggered by the value of the `ui.interactive'
621 Interactiveness is triggered by the value of the `ui.interactive'
618 configuration variable or - if it is unset - when `sys.stdin' points
622 configuration variable or - if it is unset - when `sys.stdin' points
619 to a terminal device.
623 to a terminal device.
620
624
621 This function refers to input only; for output, see `ui.formatted()'.
625 This function refers to input only; for output, see `ui.formatted()'.
622 '''
626 '''
623 i = self.configbool("ui", "interactive", None)
627 i = self.configbool("ui", "interactive", None)
624 if i is None:
628 if i is None:
625 # some environments replace stdin without implementing isatty
629 # some environments replace stdin without implementing isatty
626 # usually those are non-interactive
630 # usually those are non-interactive
627 return self._isatty(self.fin)
631 return self._isatty(self.fin)
628
632
629 return i
633 return i
630
634
631 def termwidth(self):
635 def termwidth(self):
632 '''how wide is the terminal in columns?
636 '''how wide is the terminal in columns?
633 '''
637 '''
634 if 'COLUMNS' in os.environ:
638 if 'COLUMNS' in os.environ:
635 try:
639 try:
636 return int(os.environ['COLUMNS'])
640 return int(os.environ['COLUMNS'])
637 except ValueError:
641 except ValueError:
638 pass
642 pass
639 return util.termwidth()
643 return util.termwidth()
640
644
641 def formatted(self):
645 def formatted(self):
642 '''should formatted output be used?
646 '''should formatted output be used?
643
647
644 It is often desirable to format the output to suite the output medium.
648 It is often desirable to format the output to suite the output medium.
645 Examples of this are truncating long lines or colorizing messages.
649 Examples of this are truncating long lines or colorizing messages.
646 However, this is not often not desirable when piping output into other
650 However, this is not often not desirable when piping output into other
647 utilities, e.g. `grep'.
651 utilities, e.g. `grep'.
648
652
649 Formatted output is triggered by the value of the `ui.formatted'
653 Formatted output is triggered by the value of the `ui.formatted'
650 configuration variable or - if it is unset - when `sys.stdout' points
654 configuration variable or - if it is unset - when `sys.stdout' points
651 to a terminal device. Please note that `ui.formatted' should be
655 to a terminal device. Please note that `ui.formatted' should be
652 considered an implementation detail; it is not intended for use outside
656 considered an implementation detail; it is not intended for use outside
653 Mercurial or its extensions.
657 Mercurial or its extensions.
654
658
655 This function refers to output only; for input, see `ui.interactive()'.
659 This function refers to output only; for input, see `ui.interactive()'.
656 This function always returns false when in plain mode, see `ui.plain()'.
660 This function always returns false when in plain mode, see `ui.plain()'.
657 '''
661 '''
658 if self.plain():
662 if self.plain():
659 return False
663 return False
660
664
661 i = self.configbool("ui", "formatted", None)
665 i = self.configbool("ui", "formatted", None)
662 if i is None:
666 if i is None:
663 # some environments replace stdout without implementing isatty
667 # some environments replace stdout without implementing isatty
664 # usually those are non-interactive
668 # usually those are non-interactive
665 return self._isatty(self.fout)
669 return self._isatty(self.fout)
666
670
667 return i
671 return i
668
672
669 def _readline(self, prompt=''):
673 def _readline(self, prompt=''):
670 if self._isatty(self.fin):
674 if self._isatty(self.fin):
671 try:
675 try:
672 # magically add command line editing support, where
676 # magically add command line editing support, where
673 # available
677 # available
674 import readline
678 import readline
675 # force demandimport to really load the module
679 # force demandimport to really load the module
676 readline.read_history_file
680 readline.read_history_file
677 # windows sometimes raises something other than ImportError
681 # windows sometimes raises something other than ImportError
678 except Exception:
682 except Exception:
679 pass
683 pass
680
684
681 # call write() so output goes through subclassed implementation
685 # call write() so output goes through subclassed implementation
682 # e.g. color extension on Windows
686 # e.g. color extension on Windows
683 self.write(prompt)
687 self.write(prompt)
684
688
685 # instead of trying to emulate raw_input, swap (self.fin,
689 # instead of trying to emulate raw_input, swap (self.fin,
686 # self.fout) with (sys.stdin, sys.stdout)
690 # self.fout) with (sys.stdin, sys.stdout)
687 oldin = sys.stdin
691 oldin = sys.stdin
688 oldout = sys.stdout
692 oldout = sys.stdout
689 sys.stdin = self.fin
693 sys.stdin = self.fin
690 sys.stdout = self.fout
694 sys.stdout = self.fout
691 # prompt ' ' must exist; otherwise readline may delete entire line
695 # prompt ' ' must exist; otherwise readline may delete entire line
692 # - http://bugs.python.org/issue12833
696 # - http://bugs.python.org/issue12833
693 line = raw_input(' ')
697 line = raw_input(' ')
694 sys.stdin = oldin
698 sys.stdin = oldin
695 sys.stdout = oldout
699 sys.stdout = oldout
696
700
697 # When stdin is in binary mode on Windows, it can cause
701 # When stdin is in binary mode on Windows, it can cause
698 # raw_input() to emit an extra trailing carriage return
702 # raw_input() to emit an extra trailing carriage return
699 if os.linesep == '\r\n' and line and line[-1] == '\r':
703 if os.linesep == '\r\n' and line and line[-1] == '\r':
700 line = line[:-1]
704 line = line[:-1]
701 return line
705 return line
702
706
703 def prompt(self, msg, default="y"):
707 def prompt(self, msg, default="y"):
704 """Prompt user with msg, read response.
708 """Prompt user with msg, read response.
705 If ui is not interactive, the default is returned.
709 If ui is not interactive, the default is returned.
706 """
710 """
707 if not self.interactive():
711 if not self.interactive():
708 self.write(msg, ' ', default, "\n")
712 self.write(msg, ' ', default, "\n")
709 return default
713 return default
710 try:
714 try:
711 r = self._readline(self.label(msg, 'ui.prompt'))
715 r = self._readline(self.label(msg, 'ui.prompt'))
712 if not r:
716 if not r:
713 r = default
717 r = default
714 if self.configbool('ui', 'promptecho'):
718 if self.configbool('ui', 'promptecho'):
715 self.write(r, "\n")
719 self.write(r, "\n")
716 return r
720 return r
717 except EOFError:
721 except EOFError:
718 raise util.Abort(_('response expected'))
722 raise util.Abort(_('response expected'))
719
723
720 @staticmethod
724 @staticmethod
721 def extractchoices(prompt):
725 def extractchoices(prompt):
722 """Extract prompt message and list of choices from specified prompt.
726 """Extract prompt message and list of choices from specified prompt.
723
727
724 This returns tuple "(message, choices)", and "choices" is the
728 This returns tuple "(message, choices)", and "choices" is the
725 list of tuple "(response character, text without &)".
729 list of tuple "(response character, text without &)".
726 """
730 """
727 parts = prompt.split('$$')
731 parts = prompt.split('$$')
728 msg = parts[0].rstrip(' ')
732 msg = parts[0].rstrip(' ')
729 choices = [p.strip(' ') for p in parts[1:]]
733 choices = [p.strip(' ') for p in parts[1:]]
730 return (msg,
734 return (msg,
731 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
735 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
732 for s in choices])
736 for s in choices])
733
737
734 def promptchoice(self, prompt, default=0):
738 def promptchoice(self, prompt, default=0):
735 """Prompt user with a message, read response, and ensure it matches
739 """Prompt user with a message, read response, and ensure it matches
736 one of the provided choices. The prompt is formatted as follows:
740 one of the provided choices. The prompt is formatted as follows:
737
741
738 "would you like fries with that (Yn)? $$ &Yes $$ &No"
742 "would you like fries with that (Yn)? $$ &Yes $$ &No"
739
743
740 The index of the choice is returned. Responses are case
744 The index of the choice is returned. Responses are case
741 insensitive. If ui is not interactive, the default is
745 insensitive. If ui is not interactive, the default is
742 returned.
746 returned.
743 """
747 """
744
748
745 msg, choices = self.extractchoices(prompt)
749 msg, choices = self.extractchoices(prompt)
746 resps = [r for r, t in choices]
750 resps = [r for r, t in choices]
747 while True:
751 while True:
748 r = self.prompt(msg, resps[default])
752 r = self.prompt(msg, resps[default])
749 if r.lower() in resps:
753 if r.lower() in resps:
750 return resps.index(r.lower())
754 return resps.index(r.lower())
751 self.write(_("unrecognized response\n"))
755 self.write(_("unrecognized response\n"))
752
756
753 def getpass(self, prompt=None, default=None):
757 def getpass(self, prompt=None, default=None):
754 if not self.interactive():
758 if not self.interactive():
755 return default
759 return default
756 try:
760 try:
757 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
761 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
758 # disable getpass() only if explicitly specified. it's still valid
762 # disable getpass() only if explicitly specified. it's still valid
759 # to interact with tty even if fin is not a tty.
763 # to interact with tty even if fin is not a tty.
760 if self.configbool('ui', 'nontty'):
764 if self.configbool('ui', 'nontty'):
761 return self.fin.readline().rstrip('\n')
765 return self.fin.readline().rstrip('\n')
762 else:
766 else:
763 return getpass.getpass('')
767 return getpass.getpass('')
764 except EOFError:
768 except EOFError:
765 raise util.Abort(_('response expected'))
769 raise util.Abort(_('response expected'))
766 def status(self, *msg, **opts):
770 def status(self, *msg, **opts):
767 '''write status message to output (if ui.quiet is False)
771 '''write status message to output (if ui.quiet is False)
768
772
769 This adds an output label of "ui.status".
773 This adds an output label of "ui.status".
770 '''
774 '''
771 if not self.quiet:
775 if not self.quiet:
772 opts['label'] = opts.get('label', '') + ' ui.status'
776 opts['label'] = opts.get('label', '') + ' ui.status'
773 self.write(*msg, **opts)
777 self.write(*msg, **opts)
774 def warn(self, *msg, **opts):
778 def warn(self, *msg, **opts):
775 '''write warning message to output (stderr)
779 '''write warning message to output (stderr)
776
780
777 This adds an output label of "ui.warning".
781 This adds an output label of "ui.warning".
778 '''
782 '''
779 opts['label'] = opts.get('label', '') + ' ui.warning'
783 opts['label'] = opts.get('label', '') + ' ui.warning'
780 self.write_err(*msg, **opts)
784 self.write_err(*msg, **opts)
781 def note(self, *msg, **opts):
785 def note(self, *msg, **opts):
782 '''write note to output (if ui.verbose is True)
786 '''write note to output (if ui.verbose is True)
783
787
784 This adds an output label of "ui.note".
788 This adds an output label of "ui.note".
785 '''
789 '''
786 if self.verbose:
790 if self.verbose:
787 opts['label'] = opts.get('label', '') + ' ui.note'
791 opts['label'] = opts.get('label', '') + ' ui.note'
788 self.write(*msg, **opts)
792 self.write(*msg, **opts)
789 def debug(self, *msg, **opts):
793 def debug(self, *msg, **opts):
790 '''write debug message to output (if ui.debugflag is True)
794 '''write debug message to output (if ui.debugflag is True)
791
795
792 This adds an output label of "ui.debug".
796 This adds an output label of "ui.debug".
793 '''
797 '''
794 if self.debugflag:
798 if self.debugflag:
795 opts['label'] = opts.get('label', '') + ' ui.debug'
799 opts['label'] = opts.get('label', '') + ' ui.debug'
796 self.write(*msg, **opts)
800 self.write(*msg, **opts)
797 def edit(self, text, user, extra={}, editform=None):
801 def edit(self, text, user, extra={}, editform=None):
798 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
802 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
799 text=True)
803 text=True)
800 try:
804 try:
801 f = os.fdopen(fd, "w")
805 f = os.fdopen(fd, "w")
802 f.write(text)
806 f.write(text)
803 f.close()
807 f.close()
804
808
805 environ = {'HGUSER': user}
809 environ = {'HGUSER': user}
806 if 'transplant_source' in extra:
810 if 'transplant_source' in extra:
807 environ.update({'HGREVISION': hex(extra['transplant_source'])})
811 environ.update({'HGREVISION': hex(extra['transplant_source'])})
808 for label in ('source', 'rebase_source'):
812 for label in ('source', 'rebase_source'):
809 if label in extra:
813 if label in extra:
810 environ.update({'HGREVISION': extra[label]})
814 environ.update({'HGREVISION': extra[label]})
811 break
815 break
812 if editform:
816 if editform:
813 environ.update({'HGEDITFORM': editform})
817 environ.update({'HGEDITFORM': editform})
814
818
815 editor = self.geteditor()
819 editor = self.geteditor()
816
820
817 self.system("%s \"%s\"" % (editor, name),
821 self.system("%s \"%s\"" % (editor, name),
818 environ=environ,
822 environ=environ,
819 onerr=util.Abort, errprefix=_("edit failed"))
823 onerr=util.Abort, errprefix=_("edit failed"))
820
824
821 f = open(name)
825 f = open(name)
822 t = f.read()
826 t = f.read()
823 f.close()
827 f.close()
824 finally:
828 finally:
825 os.unlink(name)
829 os.unlink(name)
826
830
827 return t
831 return t
828
832
829 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
833 def system(self, cmd, environ={}, cwd=None, onerr=None, errprefix=None):
830 '''execute shell command with appropriate output stream. command
834 '''execute shell command with appropriate output stream. command
831 output will be redirected if fout is not stdout.
835 output will be redirected if fout is not stdout.
832 '''
836 '''
833 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
837 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
834 errprefix=errprefix, out=self.fout)
838 errprefix=errprefix, out=self.fout)
835
839
836 def traceback(self, exc=None, force=False):
840 def traceback(self, exc=None, force=False):
837 '''print exception traceback if traceback printing enabled or forced.
841 '''print exception traceback if traceback printing enabled or forced.
838 only to call in exception handler. returns true if traceback
842 only to call in exception handler. returns true if traceback
839 printed.'''
843 printed.'''
840 if self.tracebackflag or force:
844 if self.tracebackflag or force:
841 if exc is None:
845 if exc is None:
842 exc = sys.exc_info()
846 exc = sys.exc_info()
843 cause = getattr(exc[1], 'cause', None)
847 cause = getattr(exc[1], 'cause', None)
844
848
845 if cause is not None:
849 if cause is not None:
846 causetb = traceback.format_tb(cause[2])
850 causetb = traceback.format_tb(cause[2])
847 exctb = traceback.format_tb(exc[2])
851 exctb = traceback.format_tb(exc[2])
848 exconly = traceback.format_exception_only(cause[0], cause[1])
852 exconly = traceback.format_exception_only(cause[0], cause[1])
849
853
850 # exclude frame where 'exc' was chained and rethrown from exctb
854 # exclude frame where 'exc' was chained and rethrown from exctb
851 self.write_err('Traceback (most recent call last):\n',
855 self.write_err('Traceback (most recent call last):\n',
852 ''.join(exctb[:-1]),
856 ''.join(exctb[:-1]),
853 ''.join(causetb),
857 ''.join(causetb),
854 ''.join(exconly))
858 ''.join(exconly))
855 else:
859 else:
856 traceback.print_exception(exc[0], exc[1], exc[2],
860 traceback.print_exception(exc[0], exc[1], exc[2],
857 file=self.ferr)
861 file=self.ferr)
858 return self.tracebackflag or force
862 return self.tracebackflag or force
859
863
860 def geteditor(self):
864 def geteditor(self):
861 '''return editor to use'''
865 '''return editor to use'''
862 if sys.platform == 'plan9':
866 if sys.platform == 'plan9':
863 # vi is the MIPS instruction simulator on Plan 9. We
867 # vi is the MIPS instruction simulator on Plan 9. We
864 # instead default to E to plumb commit messages to
868 # instead default to E to plumb commit messages to
865 # avoid confusion.
869 # avoid confusion.
866 editor = 'E'
870 editor = 'E'
867 else:
871 else:
868 editor = 'vi'
872 editor = 'vi'
869 return (os.environ.get("HGEDITOR") or
873 return (os.environ.get("HGEDITOR") or
870 self.config("ui", "editor") or
874 self.config("ui", "editor") or
871 os.environ.get("VISUAL") or
875 os.environ.get("VISUAL") or
872 os.environ.get("EDITOR", editor))
876 os.environ.get("EDITOR", editor))
873
877
874 def progress(self, topic, pos, item="", unit="", total=None):
878 def progress(self, topic, pos, item="", unit="", total=None):
875 '''show a progress message
879 '''show a progress message
876
880
877 With stock hg, this is simply a debug message that is hidden
881 With stock hg, this is simply a debug message that is hidden
878 by default, but with extensions or GUI tools it may be
882 by default, but with extensions or GUI tools it may be
879 visible. 'topic' is the current operation, 'item' is a
883 visible. 'topic' is the current operation, 'item' is a
880 non-numeric marker of the current position (i.e. the currently
884 non-numeric marker of the current position (i.e. the currently
881 in-process file), 'pos' is the current numeric position (i.e.
885 in-process file), 'pos' is the current numeric position (i.e.
882 revision, bytes, etc.), unit is a corresponding unit label,
886 revision, bytes, etc.), unit is a corresponding unit label,
883 and total is the highest expected pos.
887 and total is the highest expected pos.
884
888
885 Multiple nested topics may be active at a time.
889 Multiple nested topics may be active at a time.
886
890
887 All topics should be marked closed by setting pos to None at
891 All topics should be marked closed by setting pos to None at
888 termination.
892 termination.
889 '''
893 '''
890
894
891 if pos is None or not self.debugflag:
895 if pos is None or not self.debugflag:
892 return
896 return
893
897
894 if unit:
898 if unit:
895 unit = ' ' + unit
899 unit = ' ' + unit
896 if item:
900 if item:
897 item = ' ' + item
901 item = ' ' + item
898
902
899 if total:
903 if total:
900 pct = 100.0 * pos / total
904 pct = 100.0 * pos / total
901 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
905 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
902 % (topic, item, pos, total, unit, pct))
906 % (topic, item, pos, total, unit, pct))
903 else:
907 else:
904 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
908 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
905
909
906 def log(self, service, *msg, **opts):
910 def log(self, service, *msg, **opts):
907 '''hook for logging facility extensions
911 '''hook for logging facility extensions
908
912
909 service should be a readily-identifiable subsystem, which will
913 service should be a readily-identifiable subsystem, which will
910 allow filtering.
914 allow filtering.
911 message should be a newline-terminated string to log.
915 message should be a newline-terminated string to log.
912 '''
916 '''
913 pass
917 pass
914
918
915 def label(self, msg, label):
919 def label(self, msg, label):
916 '''style msg based on supplied label
920 '''style msg based on supplied label
917
921
918 Like ui.write(), this just returns msg unchanged, but extensions
922 Like ui.write(), this just returns msg unchanged, but extensions
919 and GUI tools can override it to allow styling output without
923 and GUI tools can override it to allow styling output without
920 writing it.
924 writing it.
921
925
922 ui.write(s, 'label') is equivalent to
926 ui.write(s, 'label') is equivalent to
923 ui.write(ui.label(s, 'label')).
927 ui.write(ui.label(s, 'label')).
924 '''
928 '''
925 return msg
929 return msg
930
931 class paths(dict):
932 """Represents a collection of paths and their configs.
933
934 Data is initially derived from ui instances and the config files they have
935 loaded.
936 """
937 def __init__(self, ui):
938 dict.__init__(self)
939
940 for name, loc in ui.configitems('paths'):
941 # No location is the same as not existing.
942 if not loc:
943 continue
944 self[name] = path(name, rawloc=loc)
945
946 def getpath(self, name, default=None):
947 """Return a ``path`` for the specified name, falling back to a default.
948
949 Returns the first of ``name`` or ``default`` that is present, or None
950 if neither is present.
951 """
952 try:
953 return self[name]
954 except KeyError:
955 if default is not None:
956 try:
957 return self[default]
958 except KeyError:
959 pass
960
961 return None
962
963 class path(object):
964 """Represents an individual path and its configuration."""
965
966 def __init__(self, name, rawloc=None):
967 """Construct a path from its config options.
968
969 ``name`` is the symbolic name of the path.
970 ``rawloc`` is the raw location, as defined in the config.
971 """
972 self.name = name
973 # We'll do more intelligent things with rawloc in the future.
974 self.loc = rawloc
General Comments 0
You need to be logged in to leave comments. Login now