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