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