##// END OF EJS Templates
ui: write prompt text in ui.prompt(), not in ui._readline()...
Yuya Nishihara -
r35974:fd54846e default
parent child Browse files
Show More
@@ -1,1835 +1,1832 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 collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import getpass
13 import getpass
14 import inspect
14 import inspect
15 import os
15 import os
16 import re
16 import re
17 import signal
17 import signal
18 import socket
18 import socket
19 import subprocess
19 import subprocess
20 import sys
20 import sys
21 import tempfile
21 import tempfile
22 import traceback
22 import traceback
23
23
24 from .i18n import _
24 from .i18n import _
25 from .node import hex
25 from .node import hex
26
26
27 from . import (
27 from . import (
28 color,
28 color,
29 config,
29 config,
30 configitems,
30 configitems,
31 encoding,
31 encoding,
32 error,
32 error,
33 formatter,
33 formatter,
34 progress,
34 progress,
35 pycompat,
35 pycompat,
36 rcutil,
36 rcutil,
37 scmutil,
37 scmutil,
38 util,
38 util,
39 )
39 )
40
40
41 urlreq = util.urlreq
41 urlreq = util.urlreq
42
42
43 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
43 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
44 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
44 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
45 if not c.isalnum())
45 if not c.isalnum())
46
46
47 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
47 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
48 tweakrc = b"""
48 tweakrc = b"""
49 [ui]
49 [ui]
50 # The rollback command is dangerous. As a rule, don't use it.
50 # The rollback command is dangerous. As a rule, don't use it.
51 rollback = False
51 rollback = False
52 # Make `hg status` report copy information
52 # Make `hg status` report copy information
53 statuscopies = yes
53 statuscopies = yes
54 # Prefer curses UIs when available. Revert to plain-text with `text`.
54 # Prefer curses UIs when available. Revert to plain-text with `text`.
55 interface = curses
55 interface = curses
56
56
57 [commands]
57 [commands]
58 # Make `hg status` emit cwd-relative paths by default.
58 # Make `hg status` emit cwd-relative paths by default.
59 status.relative = yes
59 status.relative = yes
60 # Refuse to perform an `hg update` that would cause a file content merge
60 # Refuse to perform an `hg update` that would cause a file content merge
61 update.check = noconflict
61 update.check = noconflict
62
62
63 [diff]
63 [diff]
64 git = 1
64 git = 1
65 showfunc = 1
65 showfunc = 1
66 """
66 """
67
67
68 samplehgrcs = {
68 samplehgrcs = {
69 'user':
69 'user':
70 b"""# example user config (see 'hg help config' for more info)
70 b"""# example user config (see 'hg help config' for more info)
71 [ui]
71 [ui]
72 # name and email, e.g.
72 # name and email, e.g.
73 # username = Jane Doe <jdoe@example.com>
73 # username = Jane Doe <jdoe@example.com>
74 username =
74 username =
75
75
76 # We recommend enabling tweakdefaults to get slight improvements to
76 # We recommend enabling tweakdefaults to get slight improvements to
77 # the UI over time. Make sure to set HGPLAIN in the environment when
77 # the UI over time. Make sure to set HGPLAIN in the environment when
78 # writing scripts!
78 # writing scripts!
79 # tweakdefaults = True
79 # tweakdefaults = True
80
80
81 # uncomment to disable color in command output
81 # uncomment to disable color in command output
82 # (see 'hg help color' for details)
82 # (see 'hg help color' for details)
83 # color = never
83 # color = never
84
84
85 # uncomment to disable command output pagination
85 # uncomment to disable command output pagination
86 # (see 'hg help pager' for details)
86 # (see 'hg help pager' for details)
87 # paginate = never
87 # paginate = never
88
88
89 [extensions]
89 [extensions]
90 # uncomment these lines to enable some popular extensions
90 # uncomment these lines to enable some popular extensions
91 # (see 'hg help extensions' for more info)
91 # (see 'hg help extensions' for more info)
92 #
92 #
93 # churn =
93 # churn =
94 """,
94 """,
95
95
96 'cloned':
96 'cloned':
97 b"""# example repository config (see 'hg help config' for more info)
97 b"""# example repository config (see 'hg help config' for more info)
98 [paths]
98 [paths]
99 default = %s
99 default = %s
100
100
101 # path aliases to other clones of this repo in URLs or filesystem paths
101 # path aliases to other clones of this repo in URLs or filesystem paths
102 # (see 'hg help config.paths' for more info)
102 # (see 'hg help config.paths' for more info)
103 #
103 #
104 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
104 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
105 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
105 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
106 # my-clone = /home/jdoe/jdoes-clone
106 # my-clone = /home/jdoe/jdoes-clone
107
107
108 [ui]
108 [ui]
109 # name and email (local to this repository, optional), e.g.
109 # name and email (local to this repository, optional), e.g.
110 # username = Jane Doe <jdoe@example.com>
110 # username = Jane Doe <jdoe@example.com>
111 """,
111 """,
112
112
113 'local':
113 'local':
114 b"""# example repository config (see 'hg help config' for more info)
114 b"""# example repository config (see 'hg help config' for more info)
115 [paths]
115 [paths]
116 # path aliases to other clones of this repo in URLs or filesystem paths
116 # path aliases to other clones of this repo in URLs or filesystem paths
117 # (see 'hg help config.paths' for more info)
117 # (see 'hg help config.paths' for more info)
118 #
118 #
119 # default = http://example.com/hg/example-repo
119 # default = http://example.com/hg/example-repo
120 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
120 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
121 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
121 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
122 # my-clone = /home/jdoe/jdoes-clone
122 # my-clone = /home/jdoe/jdoes-clone
123
123
124 [ui]
124 [ui]
125 # name and email (local to this repository, optional), e.g.
125 # name and email (local to this repository, optional), e.g.
126 # username = Jane Doe <jdoe@example.com>
126 # username = Jane Doe <jdoe@example.com>
127 """,
127 """,
128
128
129 'global':
129 'global':
130 b"""# example system-wide hg config (see 'hg help config' for more info)
130 b"""# example system-wide hg config (see 'hg help config' for more info)
131
131
132 [ui]
132 [ui]
133 # uncomment to disable color in command output
133 # uncomment to disable color in command output
134 # (see 'hg help color' for details)
134 # (see 'hg help color' for details)
135 # color = never
135 # color = never
136
136
137 # uncomment to disable command output pagination
137 # uncomment to disable command output pagination
138 # (see 'hg help pager' for details)
138 # (see 'hg help pager' for details)
139 # paginate = never
139 # paginate = never
140
140
141 [extensions]
141 [extensions]
142 # uncomment these lines to enable some popular extensions
142 # uncomment these lines to enable some popular extensions
143 # (see 'hg help extensions' for more info)
143 # (see 'hg help extensions' for more info)
144 #
144 #
145 # blackbox =
145 # blackbox =
146 # churn =
146 # churn =
147 """,
147 """,
148 }
148 }
149
149
150 def _maybestrurl(maybebytes):
150 def _maybestrurl(maybebytes):
151 return util.rapply(pycompat.strurl, maybebytes)
151 return util.rapply(pycompat.strurl, maybebytes)
152
152
153 def _maybebytesurl(maybestr):
153 def _maybebytesurl(maybestr):
154 return util.rapply(pycompat.bytesurl, maybestr)
154 return util.rapply(pycompat.bytesurl, maybestr)
155
155
156 class httppasswordmgrdbproxy(object):
156 class httppasswordmgrdbproxy(object):
157 """Delays loading urllib2 until it's needed."""
157 """Delays loading urllib2 until it's needed."""
158 def __init__(self):
158 def __init__(self):
159 self._mgr = None
159 self._mgr = None
160
160
161 def _get_mgr(self):
161 def _get_mgr(self):
162 if self._mgr is None:
162 if self._mgr is None:
163 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
163 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
164 return self._mgr
164 return self._mgr
165
165
166 def add_password(self, realm, uris, user, passwd):
166 def add_password(self, realm, uris, user, passwd):
167 return self._get_mgr().add_password(
167 return self._get_mgr().add_password(
168 _maybestrurl(realm), _maybestrurl(uris),
168 _maybestrurl(realm), _maybestrurl(uris),
169 _maybestrurl(user), _maybestrurl(passwd))
169 _maybestrurl(user), _maybestrurl(passwd))
170
170
171 def find_user_password(self, realm, uri):
171 def find_user_password(self, realm, uri):
172 mgr = self._get_mgr()
172 mgr = self._get_mgr()
173 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
173 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
174 _maybestrurl(uri)))
174 _maybestrurl(uri)))
175
175
176 def _catchterm(*args):
176 def _catchterm(*args):
177 raise error.SignalInterrupt
177 raise error.SignalInterrupt
178
178
179 # unique object used to detect no default value has been provided when
179 # unique object used to detect no default value has been provided when
180 # retrieving configuration value.
180 # retrieving configuration value.
181 _unset = object()
181 _unset = object()
182
182
183 # _reqexithandlers: callbacks run at the end of a request
183 # _reqexithandlers: callbacks run at the end of a request
184 _reqexithandlers = []
184 _reqexithandlers = []
185
185
186 class ui(object):
186 class ui(object):
187 def __init__(self, src=None):
187 def __init__(self, src=None):
188 """Create a fresh new ui object if no src given
188 """Create a fresh new ui object if no src given
189
189
190 Use uimod.ui.load() to create a ui which knows global and user configs.
190 Use uimod.ui.load() to create a ui which knows global and user configs.
191 In most cases, you should use ui.copy() to create a copy of an existing
191 In most cases, you should use ui.copy() to create a copy of an existing
192 ui object.
192 ui object.
193 """
193 """
194 # _buffers: used for temporary capture of output
194 # _buffers: used for temporary capture of output
195 self._buffers = []
195 self._buffers = []
196 # 3-tuple describing how each buffer in the stack behaves.
196 # 3-tuple describing how each buffer in the stack behaves.
197 # Values are (capture stderr, capture subprocesses, apply labels).
197 # Values are (capture stderr, capture subprocesses, apply labels).
198 self._bufferstates = []
198 self._bufferstates = []
199 # When a buffer is active, defines whether we are expanding labels.
199 # When a buffer is active, defines whether we are expanding labels.
200 # This exists to prevent an extra list lookup.
200 # This exists to prevent an extra list lookup.
201 self._bufferapplylabels = None
201 self._bufferapplylabels = None
202 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
202 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
203 self._reportuntrusted = True
203 self._reportuntrusted = True
204 self._knownconfig = configitems.coreitems
204 self._knownconfig = configitems.coreitems
205 self._ocfg = config.config() # overlay
205 self._ocfg = config.config() # overlay
206 self._tcfg = config.config() # trusted
206 self._tcfg = config.config() # trusted
207 self._ucfg = config.config() # untrusted
207 self._ucfg = config.config() # untrusted
208 self._trustusers = set()
208 self._trustusers = set()
209 self._trustgroups = set()
209 self._trustgroups = set()
210 self.callhooks = True
210 self.callhooks = True
211 # Insecure server connections requested.
211 # Insecure server connections requested.
212 self.insecureconnections = False
212 self.insecureconnections = False
213 # Blocked time
213 # Blocked time
214 self.logblockedtimes = False
214 self.logblockedtimes = False
215 # color mode: see mercurial/color.py for possible value
215 # color mode: see mercurial/color.py for possible value
216 self._colormode = None
216 self._colormode = None
217 self._terminfoparams = {}
217 self._terminfoparams = {}
218 self._styles = {}
218 self._styles = {}
219
219
220 if src:
220 if src:
221 self.fout = src.fout
221 self.fout = src.fout
222 self.ferr = src.ferr
222 self.ferr = src.ferr
223 self.fin = src.fin
223 self.fin = src.fin
224 self.pageractive = src.pageractive
224 self.pageractive = src.pageractive
225 self._disablepager = src._disablepager
225 self._disablepager = src._disablepager
226 self._tweaked = src._tweaked
226 self._tweaked = src._tweaked
227
227
228 self._tcfg = src._tcfg.copy()
228 self._tcfg = src._tcfg.copy()
229 self._ucfg = src._ucfg.copy()
229 self._ucfg = src._ucfg.copy()
230 self._ocfg = src._ocfg.copy()
230 self._ocfg = src._ocfg.copy()
231 self._trustusers = src._trustusers.copy()
231 self._trustusers = src._trustusers.copy()
232 self._trustgroups = src._trustgroups.copy()
232 self._trustgroups = src._trustgroups.copy()
233 self.environ = src.environ
233 self.environ = src.environ
234 self.callhooks = src.callhooks
234 self.callhooks = src.callhooks
235 self.insecureconnections = src.insecureconnections
235 self.insecureconnections = src.insecureconnections
236 self._colormode = src._colormode
236 self._colormode = src._colormode
237 self._terminfoparams = src._terminfoparams.copy()
237 self._terminfoparams = src._terminfoparams.copy()
238 self._styles = src._styles.copy()
238 self._styles = src._styles.copy()
239
239
240 self.fixconfig()
240 self.fixconfig()
241
241
242 self.httppasswordmgrdb = src.httppasswordmgrdb
242 self.httppasswordmgrdb = src.httppasswordmgrdb
243 self._blockedtimes = src._blockedtimes
243 self._blockedtimes = src._blockedtimes
244 else:
244 else:
245 self.fout = util.stdout
245 self.fout = util.stdout
246 self.ferr = util.stderr
246 self.ferr = util.stderr
247 self.fin = util.stdin
247 self.fin = util.stdin
248 self.pageractive = False
248 self.pageractive = False
249 self._disablepager = False
249 self._disablepager = False
250 self._tweaked = False
250 self._tweaked = False
251
251
252 # shared read-only environment
252 # shared read-only environment
253 self.environ = encoding.environ
253 self.environ = encoding.environ
254
254
255 self.httppasswordmgrdb = httppasswordmgrdbproxy()
255 self.httppasswordmgrdb = httppasswordmgrdbproxy()
256 self._blockedtimes = collections.defaultdict(int)
256 self._blockedtimes = collections.defaultdict(int)
257
257
258 allowed = self.configlist('experimental', 'exportableenviron')
258 allowed = self.configlist('experimental', 'exportableenviron')
259 if '*' in allowed:
259 if '*' in allowed:
260 self._exportableenviron = self.environ
260 self._exportableenviron = self.environ
261 else:
261 else:
262 self._exportableenviron = {}
262 self._exportableenviron = {}
263 for k in allowed:
263 for k in allowed:
264 if k in self.environ:
264 if k in self.environ:
265 self._exportableenviron[k] = self.environ[k]
265 self._exportableenviron[k] = self.environ[k]
266
266
267 @classmethod
267 @classmethod
268 def load(cls):
268 def load(cls):
269 """Create a ui and load global and user configs"""
269 """Create a ui and load global and user configs"""
270 u = cls()
270 u = cls()
271 # we always trust global config files and environment variables
271 # we always trust global config files and environment variables
272 for t, f in rcutil.rccomponents():
272 for t, f in rcutil.rccomponents():
273 if t == 'path':
273 if t == 'path':
274 u.readconfig(f, trust=True)
274 u.readconfig(f, trust=True)
275 elif t == 'items':
275 elif t == 'items':
276 sections = set()
276 sections = set()
277 for section, name, value, source in f:
277 for section, name, value, source in f:
278 # do not set u._ocfg
278 # do not set u._ocfg
279 # XXX clean this up once immutable config object is a thing
279 # XXX clean this up once immutable config object is a thing
280 u._tcfg.set(section, name, value, source)
280 u._tcfg.set(section, name, value, source)
281 u._ucfg.set(section, name, value, source)
281 u._ucfg.set(section, name, value, source)
282 sections.add(section)
282 sections.add(section)
283 for section in sections:
283 for section in sections:
284 u.fixconfig(section=section)
284 u.fixconfig(section=section)
285 else:
285 else:
286 raise error.ProgrammingError('unknown rctype: %s' % t)
286 raise error.ProgrammingError('unknown rctype: %s' % t)
287 u._maybetweakdefaults()
287 u._maybetweakdefaults()
288 return u
288 return u
289
289
290 def _maybetweakdefaults(self):
290 def _maybetweakdefaults(self):
291 if not self.configbool('ui', 'tweakdefaults'):
291 if not self.configbool('ui', 'tweakdefaults'):
292 return
292 return
293 if self._tweaked or self.plain('tweakdefaults'):
293 if self._tweaked or self.plain('tweakdefaults'):
294 return
294 return
295
295
296 # Note: it is SUPER IMPORTANT that you set self._tweaked to
296 # Note: it is SUPER IMPORTANT that you set self._tweaked to
297 # True *before* any calls to setconfig(), otherwise you'll get
297 # True *before* any calls to setconfig(), otherwise you'll get
298 # infinite recursion between setconfig and this method.
298 # infinite recursion between setconfig and this method.
299 #
299 #
300 # TODO: We should extract an inner method in setconfig() to
300 # TODO: We should extract an inner method in setconfig() to
301 # avoid this weirdness.
301 # avoid this weirdness.
302 self._tweaked = True
302 self._tweaked = True
303 tmpcfg = config.config()
303 tmpcfg = config.config()
304 tmpcfg.parse('<tweakdefaults>', tweakrc)
304 tmpcfg.parse('<tweakdefaults>', tweakrc)
305 for section in tmpcfg:
305 for section in tmpcfg:
306 for name, value in tmpcfg.items(section):
306 for name, value in tmpcfg.items(section):
307 if not self.hasconfig(section, name):
307 if not self.hasconfig(section, name):
308 self.setconfig(section, name, value, "<tweakdefaults>")
308 self.setconfig(section, name, value, "<tweakdefaults>")
309
309
310 def copy(self):
310 def copy(self):
311 return self.__class__(self)
311 return self.__class__(self)
312
312
313 def resetstate(self):
313 def resetstate(self):
314 """Clear internal state that shouldn't persist across commands"""
314 """Clear internal state that shouldn't persist across commands"""
315 if self._progbar:
315 if self._progbar:
316 self._progbar.resetstate() # reset last-print time of progress bar
316 self._progbar.resetstate() # reset last-print time of progress bar
317 self.httppasswordmgrdb = httppasswordmgrdbproxy()
317 self.httppasswordmgrdb = httppasswordmgrdbproxy()
318
318
319 @contextlib.contextmanager
319 @contextlib.contextmanager
320 def timeblockedsection(self, key):
320 def timeblockedsection(self, key):
321 # this is open-coded below - search for timeblockedsection to find them
321 # this is open-coded below - search for timeblockedsection to find them
322 starttime = util.timer()
322 starttime = util.timer()
323 try:
323 try:
324 yield
324 yield
325 finally:
325 finally:
326 self._blockedtimes[key + '_blocked'] += \
326 self._blockedtimes[key + '_blocked'] += \
327 (util.timer() - starttime) * 1000
327 (util.timer() - starttime) * 1000
328
328
329 def formatter(self, topic, opts):
329 def formatter(self, topic, opts):
330 return formatter.formatter(self, self, topic, opts)
330 return formatter.formatter(self, self, topic, opts)
331
331
332 def _trusted(self, fp, f):
332 def _trusted(self, fp, f):
333 st = util.fstat(fp)
333 st = util.fstat(fp)
334 if util.isowner(st):
334 if util.isowner(st):
335 return True
335 return True
336
336
337 tusers, tgroups = self._trustusers, self._trustgroups
337 tusers, tgroups = self._trustusers, self._trustgroups
338 if '*' in tusers or '*' in tgroups:
338 if '*' in tusers or '*' in tgroups:
339 return True
339 return True
340
340
341 user = util.username(st.st_uid)
341 user = util.username(st.st_uid)
342 group = util.groupname(st.st_gid)
342 group = util.groupname(st.st_gid)
343 if user in tusers or group in tgroups or user == util.username():
343 if user in tusers or group in tgroups or user == util.username():
344 return True
344 return True
345
345
346 if self._reportuntrusted:
346 if self._reportuntrusted:
347 self.warn(_('not trusting file %s from untrusted '
347 self.warn(_('not trusting file %s from untrusted '
348 'user %s, group %s\n') % (f, user, group))
348 'user %s, group %s\n') % (f, user, group))
349 return False
349 return False
350
350
351 def readconfig(self, filename, root=None, trust=False,
351 def readconfig(self, filename, root=None, trust=False,
352 sections=None, remap=None):
352 sections=None, remap=None):
353 try:
353 try:
354 fp = open(filename, u'rb')
354 fp = open(filename, u'rb')
355 except IOError:
355 except IOError:
356 if not sections: # ignore unless we were looking for something
356 if not sections: # ignore unless we were looking for something
357 return
357 return
358 raise
358 raise
359
359
360 cfg = config.config()
360 cfg = config.config()
361 trusted = sections or trust or self._trusted(fp, filename)
361 trusted = sections or trust or self._trusted(fp, filename)
362
362
363 try:
363 try:
364 cfg.read(filename, fp, sections=sections, remap=remap)
364 cfg.read(filename, fp, sections=sections, remap=remap)
365 fp.close()
365 fp.close()
366 except error.ConfigError as inst:
366 except error.ConfigError as inst:
367 if trusted:
367 if trusted:
368 raise
368 raise
369 self.warn(_("ignored: %s\n") % str(inst))
369 self.warn(_("ignored: %s\n") % str(inst))
370
370
371 if self.plain():
371 if self.plain():
372 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
372 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
373 'logtemplate', 'statuscopies', 'style',
373 'logtemplate', 'statuscopies', 'style',
374 'traceback', 'verbose'):
374 'traceback', 'verbose'):
375 if k in cfg['ui']:
375 if k in cfg['ui']:
376 del cfg['ui'][k]
376 del cfg['ui'][k]
377 for k, v in cfg.items('defaults'):
377 for k, v in cfg.items('defaults'):
378 del cfg['defaults'][k]
378 del cfg['defaults'][k]
379 for k, v in cfg.items('commands'):
379 for k, v in cfg.items('commands'):
380 del cfg['commands'][k]
380 del cfg['commands'][k]
381 # Don't remove aliases from the configuration if in the exceptionlist
381 # Don't remove aliases from the configuration if in the exceptionlist
382 if self.plain('alias'):
382 if self.plain('alias'):
383 for k, v in cfg.items('alias'):
383 for k, v in cfg.items('alias'):
384 del cfg['alias'][k]
384 del cfg['alias'][k]
385 if self.plain('revsetalias'):
385 if self.plain('revsetalias'):
386 for k, v in cfg.items('revsetalias'):
386 for k, v in cfg.items('revsetalias'):
387 del cfg['revsetalias'][k]
387 del cfg['revsetalias'][k]
388 if self.plain('templatealias'):
388 if self.plain('templatealias'):
389 for k, v in cfg.items('templatealias'):
389 for k, v in cfg.items('templatealias'):
390 del cfg['templatealias'][k]
390 del cfg['templatealias'][k]
391
391
392 if trusted:
392 if trusted:
393 self._tcfg.update(cfg)
393 self._tcfg.update(cfg)
394 self._tcfg.update(self._ocfg)
394 self._tcfg.update(self._ocfg)
395 self._ucfg.update(cfg)
395 self._ucfg.update(cfg)
396 self._ucfg.update(self._ocfg)
396 self._ucfg.update(self._ocfg)
397
397
398 if root is None:
398 if root is None:
399 root = os.path.expanduser('~')
399 root = os.path.expanduser('~')
400 self.fixconfig(root=root)
400 self.fixconfig(root=root)
401
401
402 def fixconfig(self, root=None, section=None):
402 def fixconfig(self, root=None, section=None):
403 if section in (None, 'paths'):
403 if section in (None, 'paths'):
404 # expand vars and ~
404 # expand vars and ~
405 # translate paths relative to root (or home) into absolute paths
405 # translate paths relative to root (or home) into absolute paths
406 root = root or pycompat.getcwd()
406 root = root or pycompat.getcwd()
407 for c in self._tcfg, self._ucfg, self._ocfg:
407 for c in self._tcfg, self._ucfg, self._ocfg:
408 for n, p in c.items('paths'):
408 for n, p in c.items('paths'):
409 # Ignore sub-options.
409 # Ignore sub-options.
410 if ':' in n:
410 if ':' in n:
411 continue
411 continue
412 if not p:
412 if not p:
413 continue
413 continue
414 if '%%' in p:
414 if '%%' in p:
415 s = self.configsource('paths', n) or 'none'
415 s = self.configsource('paths', n) or 'none'
416 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
416 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
417 % (n, p, s))
417 % (n, p, s))
418 p = p.replace('%%', '%')
418 p = p.replace('%%', '%')
419 p = util.expandpath(p)
419 p = util.expandpath(p)
420 if not util.hasscheme(p) and not os.path.isabs(p):
420 if not util.hasscheme(p) and not os.path.isabs(p):
421 p = os.path.normpath(os.path.join(root, p))
421 p = os.path.normpath(os.path.join(root, p))
422 c.set("paths", n, p)
422 c.set("paths", n, p)
423
423
424 if section in (None, 'ui'):
424 if section in (None, 'ui'):
425 # update ui options
425 # update ui options
426 self.debugflag = self.configbool('ui', 'debug')
426 self.debugflag = self.configbool('ui', 'debug')
427 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
427 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
428 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
428 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
429 if self.verbose and self.quiet:
429 if self.verbose and self.quiet:
430 self.quiet = self.verbose = False
430 self.quiet = self.verbose = False
431 self._reportuntrusted = self.debugflag or self.configbool("ui",
431 self._reportuntrusted = self.debugflag or self.configbool("ui",
432 "report_untrusted")
432 "report_untrusted")
433 self.tracebackflag = self.configbool('ui', 'traceback')
433 self.tracebackflag = self.configbool('ui', 'traceback')
434 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
434 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
435
435
436 if section in (None, 'trusted'):
436 if section in (None, 'trusted'):
437 # update trust information
437 # update trust information
438 self._trustusers.update(self.configlist('trusted', 'users'))
438 self._trustusers.update(self.configlist('trusted', 'users'))
439 self._trustgroups.update(self.configlist('trusted', 'groups'))
439 self._trustgroups.update(self.configlist('trusted', 'groups'))
440
440
441 def backupconfig(self, section, item):
441 def backupconfig(self, section, item):
442 return (self._ocfg.backup(section, item),
442 return (self._ocfg.backup(section, item),
443 self._tcfg.backup(section, item),
443 self._tcfg.backup(section, item),
444 self._ucfg.backup(section, item),)
444 self._ucfg.backup(section, item),)
445 def restoreconfig(self, data):
445 def restoreconfig(self, data):
446 self._ocfg.restore(data[0])
446 self._ocfg.restore(data[0])
447 self._tcfg.restore(data[1])
447 self._tcfg.restore(data[1])
448 self._ucfg.restore(data[2])
448 self._ucfg.restore(data[2])
449
449
450 def setconfig(self, section, name, value, source=''):
450 def setconfig(self, section, name, value, source=''):
451 for cfg in (self._ocfg, self._tcfg, self._ucfg):
451 for cfg in (self._ocfg, self._tcfg, self._ucfg):
452 cfg.set(section, name, value, source)
452 cfg.set(section, name, value, source)
453 self.fixconfig(section=section)
453 self.fixconfig(section=section)
454 self._maybetweakdefaults()
454 self._maybetweakdefaults()
455
455
456 def _data(self, untrusted):
456 def _data(self, untrusted):
457 return untrusted and self._ucfg or self._tcfg
457 return untrusted and self._ucfg or self._tcfg
458
458
459 def configsource(self, section, name, untrusted=False):
459 def configsource(self, section, name, untrusted=False):
460 return self._data(untrusted).source(section, name)
460 return self._data(untrusted).source(section, name)
461
461
462 def config(self, section, name, default=_unset, untrusted=False):
462 def config(self, section, name, default=_unset, untrusted=False):
463 """return the plain string version of a config"""
463 """return the plain string version of a config"""
464 value = self._config(section, name, default=default,
464 value = self._config(section, name, default=default,
465 untrusted=untrusted)
465 untrusted=untrusted)
466 if value is _unset:
466 if value is _unset:
467 return None
467 return None
468 return value
468 return value
469
469
470 def _config(self, section, name, default=_unset, untrusted=False):
470 def _config(self, section, name, default=_unset, untrusted=False):
471 value = itemdefault = default
471 value = itemdefault = default
472 item = self._knownconfig.get(section, {}).get(name)
472 item = self._knownconfig.get(section, {}).get(name)
473 alternates = [(section, name)]
473 alternates = [(section, name)]
474
474
475 if item is not None:
475 if item is not None:
476 alternates.extend(item.alias)
476 alternates.extend(item.alias)
477 if callable(item.default):
477 if callable(item.default):
478 itemdefault = item.default()
478 itemdefault = item.default()
479 else:
479 else:
480 itemdefault = item.default
480 itemdefault = item.default
481 else:
481 else:
482 msg = ("accessing unregistered config item: '%s.%s'")
482 msg = ("accessing unregistered config item: '%s.%s'")
483 msg %= (section, name)
483 msg %= (section, name)
484 self.develwarn(msg, 2, 'warn-config-unknown')
484 self.develwarn(msg, 2, 'warn-config-unknown')
485
485
486 if default is _unset:
486 if default is _unset:
487 if item is None:
487 if item is None:
488 value = default
488 value = default
489 elif item.default is configitems.dynamicdefault:
489 elif item.default is configitems.dynamicdefault:
490 value = None
490 value = None
491 msg = "config item requires an explicit default value: '%s.%s'"
491 msg = "config item requires an explicit default value: '%s.%s'"
492 msg %= (section, name)
492 msg %= (section, name)
493 self.develwarn(msg, 2, 'warn-config-default')
493 self.develwarn(msg, 2, 'warn-config-default')
494 else:
494 else:
495 value = itemdefault
495 value = itemdefault
496 elif (item is not None
496 elif (item is not None
497 and item.default is not configitems.dynamicdefault
497 and item.default is not configitems.dynamicdefault
498 and default != itemdefault):
498 and default != itemdefault):
499 msg = ("specifying a mismatched default value for a registered "
499 msg = ("specifying a mismatched default value for a registered "
500 "config item: '%s.%s' '%s'")
500 "config item: '%s.%s' '%s'")
501 msg %= (section, name, default)
501 msg %= (section, name, default)
502 self.develwarn(msg, 2, 'warn-config-default')
502 self.develwarn(msg, 2, 'warn-config-default')
503
503
504 for s, n in alternates:
504 for s, n in alternates:
505 candidate = self._data(untrusted).get(s, n, None)
505 candidate = self._data(untrusted).get(s, n, None)
506 if candidate is not None:
506 if candidate is not None:
507 value = candidate
507 value = candidate
508 section = s
508 section = s
509 name = n
509 name = n
510 break
510 break
511
511
512 if self.debugflag and not untrusted and self._reportuntrusted:
512 if self.debugflag and not untrusted and self._reportuntrusted:
513 for s, n in alternates:
513 for s, n in alternates:
514 uvalue = self._ucfg.get(s, n)
514 uvalue = self._ucfg.get(s, n)
515 if uvalue is not None and uvalue != value:
515 if uvalue is not None and uvalue != value:
516 self.debug("ignoring untrusted configuration option "
516 self.debug("ignoring untrusted configuration option "
517 "%s.%s = %s\n" % (s, n, uvalue))
517 "%s.%s = %s\n" % (s, n, uvalue))
518 return value
518 return value
519
519
520 def configsuboptions(self, section, name, default=_unset, untrusted=False):
520 def configsuboptions(self, section, name, default=_unset, untrusted=False):
521 """Get a config option and all sub-options.
521 """Get a config option and all sub-options.
522
522
523 Some config options have sub-options that are declared with the
523 Some config options have sub-options that are declared with the
524 format "key:opt = value". This method is used to return the main
524 format "key:opt = value". This method is used to return the main
525 option and all its declared sub-options.
525 option and all its declared sub-options.
526
526
527 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
527 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
528 is a dict of defined sub-options where keys and values are strings.
528 is a dict of defined sub-options where keys and values are strings.
529 """
529 """
530 main = self.config(section, name, default, untrusted=untrusted)
530 main = self.config(section, name, default, untrusted=untrusted)
531 data = self._data(untrusted)
531 data = self._data(untrusted)
532 sub = {}
532 sub = {}
533 prefix = '%s:' % name
533 prefix = '%s:' % name
534 for k, v in data.items(section):
534 for k, v in data.items(section):
535 if k.startswith(prefix):
535 if k.startswith(prefix):
536 sub[k[len(prefix):]] = v
536 sub[k[len(prefix):]] = v
537
537
538 if self.debugflag and not untrusted and self._reportuntrusted:
538 if self.debugflag and not untrusted and self._reportuntrusted:
539 for k, v in sub.items():
539 for k, v in sub.items():
540 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
540 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
541 if uvalue is not None and uvalue != v:
541 if uvalue is not None and uvalue != v:
542 self.debug('ignoring untrusted configuration option '
542 self.debug('ignoring untrusted configuration option '
543 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
543 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
544
544
545 return main, sub
545 return main, sub
546
546
547 def configpath(self, section, name, default=_unset, untrusted=False):
547 def configpath(self, section, name, default=_unset, untrusted=False):
548 'get a path config item, expanded relative to repo root or config file'
548 'get a path config item, expanded relative to repo root or config file'
549 v = self.config(section, name, default, untrusted)
549 v = self.config(section, name, default, untrusted)
550 if v is None:
550 if v is None:
551 return None
551 return None
552 if not os.path.isabs(v) or "://" not in v:
552 if not os.path.isabs(v) or "://" not in v:
553 src = self.configsource(section, name, untrusted)
553 src = self.configsource(section, name, untrusted)
554 if ':' in src:
554 if ':' in src:
555 base = os.path.dirname(src.rsplit(':')[0])
555 base = os.path.dirname(src.rsplit(':')[0])
556 v = os.path.join(base, os.path.expanduser(v))
556 v = os.path.join(base, os.path.expanduser(v))
557 return v
557 return v
558
558
559 def configbool(self, section, name, default=_unset, untrusted=False):
559 def configbool(self, section, name, default=_unset, untrusted=False):
560 """parse a configuration element as a boolean
560 """parse a configuration element as a boolean
561
561
562 >>> u = ui(); s = b'foo'
562 >>> u = ui(); s = b'foo'
563 >>> u.setconfig(s, b'true', b'yes')
563 >>> u.setconfig(s, b'true', b'yes')
564 >>> u.configbool(s, b'true')
564 >>> u.configbool(s, b'true')
565 True
565 True
566 >>> u.setconfig(s, b'false', b'no')
566 >>> u.setconfig(s, b'false', b'no')
567 >>> u.configbool(s, b'false')
567 >>> u.configbool(s, b'false')
568 False
568 False
569 >>> u.configbool(s, b'unknown')
569 >>> u.configbool(s, b'unknown')
570 False
570 False
571 >>> u.configbool(s, b'unknown', True)
571 >>> u.configbool(s, b'unknown', True)
572 True
572 True
573 >>> u.setconfig(s, b'invalid', b'somevalue')
573 >>> u.setconfig(s, b'invalid', b'somevalue')
574 >>> u.configbool(s, b'invalid')
574 >>> u.configbool(s, b'invalid')
575 Traceback (most recent call last):
575 Traceback (most recent call last):
576 ...
576 ...
577 ConfigError: foo.invalid is not a boolean ('somevalue')
577 ConfigError: foo.invalid is not a boolean ('somevalue')
578 """
578 """
579
579
580 v = self._config(section, name, default, untrusted=untrusted)
580 v = self._config(section, name, default, untrusted=untrusted)
581 if v is None:
581 if v is None:
582 return v
582 return v
583 if v is _unset:
583 if v is _unset:
584 if default is _unset:
584 if default is _unset:
585 return False
585 return False
586 return default
586 return default
587 if isinstance(v, bool):
587 if isinstance(v, bool):
588 return v
588 return v
589 b = util.parsebool(v)
589 b = util.parsebool(v)
590 if b is None:
590 if b is None:
591 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
591 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
592 % (section, name, v))
592 % (section, name, v))
593 return b
593 return b
594
594
595 def configwith(self, convert, section, name, default=_unset,
595 def configwith(self, convert, section, name, default=_unset,
596 desc=None, untrusted=False):
596 desc=None, untrusted=False):
597 """parse a configuration element with a conversion function
597 """parse a configuration element with a conversion function
598
598
599 >>> u = ui(); s = b'foo'
599 >>> u = ui(); s = b'foo'
600 >>> u.setconfig(s, b'float1', b'42')
600 >>> u.setconfig(s, b'float1', b'42')
601 >>> u.configwith(float, s, b'float1')
601 >>> u.configwith(float, s, b'float1')
602 42.0
602 42.0
603 >>> u.setconfig(s, b'float2', b'-4.25')
603 >>> u.setconfig(s, b'float2', b'-4.25')
604 >>> u.configwith(float, s, b'float2')
604 >>> u.configwith(float, s, b'float2')
605 -4.25
605 -4.25
606 >>> u.configwith(float, s, b'unknown', 7)
606 >>> u.configwith(float, s, b'unknown', 7)
607 7.0
607 7.0
608 >>> u.setconfig(s, b'invalid', b'somevalue')
608 >>> u.setconfig(s, b'invalid', b'somevalue')
609 >>> u.configwith(float, s, b'invalid')
609 >>> u.configwith(float, s, b'invalid')
610 Traceback (most recent call last):
610 Traceback (most recent call last):
611 ...
611 ...
612 ConfigError: foo.invalid is not a valid float ('somevalue')
612 ConfigError: foo.invalid is not a valid float ('somevalue')
613 >>> u.configwith(float, s, b'invalid', desc=b'womble')
613 >>> u.configwith(float, s, b'invalid', desc=b'womble')
614 Traceback (most recent call last):
614 Traceback (most recent call last):
615 ...
615 ...
616 ConfigError: foo.invalid is not a valid womble ('somevalue')
616 ConfigError: foo.invalid is not a valid womble ('somevalue')
617 """
617 """
618
618
619 v = self.config(section, name, default, untrusted)
619 v = self.config(section, name, default, untrusted)
620 if v is None:
620 if v is None:
621 return v # do not attempt to convert None
621 return v # do not attempt to convert None
622 try:
622 try:
623 return convert(v)
623 return convert(v)
624 except (ValueError, error.ParseError):
624 except (ValueError, error.ParseError):
625 if desc is None:
625 if desc is None:
626 desc = pycompat.sysbytes(convert.__name__)
626 desc = pycompat.sysbytes(convert.__name__)
627 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
627 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
628 % (section, name, desc, v))
628 % (section, name, desc, v))
629
629
630 def configint(self, section, name, default=_unset, untrusted=False):
630 def configint(self, section, name, default=_unset, untrusted=False):
631 """parse a configuration element as an integer
631 """parse a configuration element as an integer
632
632
633 >>> u = ui(); s = b'foo'
633 >>> u = ui(); s = b'foo'
634 >>> u.setconfig(s, b'int1', b'42')
634 >>> u.setconfig(s, b'int1', b'42')
635 >>> u.configint(s, b'int1')
635 >>> u.configint(s, b'int1')
636 42
636 42
637 >>> u.setconfig(s, b'int2', b'-42')
637 >>> u.setconfig(s, b'int2', b'-42')
638 >>> u.configint(s, b'int2')
638 >>> u.configint(s, b'int2')
639 -42
639 -42
640 >>> u.configint(s, b'unknown', 7)
640 >>> u.configint(s, b'unknown', 7)
641 7
641 7
642 >>> u.setconfig(s, b'invalid', b'somevalue')
642 >>> u.setconfig(s, b'invalid', b'somevalue')
643 >>> u.configint(s, b'invalid')
643 >>> u.configint(s, b'invalid')
644 Traceback (most recent call last):
644 Traceback (most recent call last):
645 ...
645 ...
646 ConfigError: foo.invalid is not a valid integer ('somevalue')
646 ConfigError: foo.invalid is not a valid integer ('somevalue')
647 """
647 """
648
648
649 return self.configwith(int, section, name, default, 'integer',
649 return self.configwith(int, section, name, default, 'integer',
650 untrusted)
650 untrusted)
651
651
652 def configbytes(self, section, name, default=_unset, untrusted=False):
652 def configbytes(self, section, name, default=_unset, untrusted=False):
653 """parse a configuration element as a quantity in bytes
653 """parse a configuration element as a quantity in bytes
654
654
655 Units can be specified as b (bytes), k or kb (kilobytes), m or
655 Units can be specified as b (bytes), k or kb (kilobytes), m or
656 mb (megabytes), g or gb (gigabytes).
656 mb (megabytes), g or gb (gigabytes).
657
657
658 >>> u = ui(); s = b'foo'
658 >>> u = ui(); s = b'foo'
659 >>> u.setconfig(s, b'val1', b'42')
659 >>> u.setconfig(s, b'val1', b'42')
660 >>> u.configbytes(s, b'val1')
660 >>> u.configbytes(s, b'val1')
661 42
661 42
662 >>> u.setconfig(s, b'val2', b'42.5 kb')
662 >>> u.setconfig(s, b'val2', b'42.5 kb')
663 >>> u.configbytes(s, b'val2')
663 >>> u.configbytes(s, b'val2')
664 43520
664 43520
665 >>> u.configbytes(s, b'unknown', b'7 MB')
665 >>> u.configbytes(s, b'unknown', b'7 MB')
666 7340032
666 7340032
667 >>> u.setconfig(s, b'invalid', b'somevalue')
667 >>> u.setconfig(s, b'invalid', b'somevalue')
668 >>> u.configbytes(s, b'invalid')
668 >>> u.configbytes(s, b'invalid')
669 Traceback (most recent call last):
669 Traceback (most recent call last):
670 ...
670 ...
671 ConfigError: foo.invalid is not a byte quantity ('somevalue')
671 ConfigError: foo.invalid is not a byte quantity ('somevalue')
672 """
672 """
673
673
674 value = self._config(section, name, default, untrusted)
674 value = self._config(section, name, default, untrusted)
675 if value is _unset:
675 if value is _unset:
676 if default is _unset:
676 if default is _unset:
677 default = 0
677 default = 0
678 value = default
678 value = default
679 if not isinstance(value, bytes):
679 if not isinstance(value, bytes):
680 return value
680 return value
681 try:
681 try:
682 return util.sizetoint(value)
682 return util.sizetoint(value)
683 except error.ParseError:
683 except error.ParseError:
684 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
684 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
685 % (section, name, value))
685 % (section, name, value))
686
686
687 def configlist(self, section, name, default=_unset, untrusted=False):
687 def configlist(self, section, name, default=_unset, untrusted=False):
688 """parse a configuration element as a list of comma/space separated
688 """parse a configuration element as a list of comma/space separated
689 strings
689 strings
690
690
691 >>> u = ui(); s = b'foo'
691 >>> u = ui(); s = b'foo'
692 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
692 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
693 >>> u.configlist(s, b'list1')
693 >>> u.configlist(s, b'list1')
694 ['this', 'is', 'a small', 'test']
694 ['this', 'is', 'a small', 'test']
695 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
695 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
696 >>> u.configlist(s, b'list2')
696 >>> u.configlist(s, b'list2')
697 ['this', 'is', 'a small', 'test']
697 ['this', 'is', 'a small', 'test']
698 """
698 """
699 # default is not always a list
699 # default is not always a list
700 v = self.configwith(config.parselist, section, name, default,
700 v = self.configwith(config.parselist, section, name, default,
701 'list', untrusted)
701 'list', untrusted)
702 if isinstance(v, bytes):
702 if isinstance(v, bytes):
703 return config.parselist(v)
703 return config.parselist(v)
704 elif v is None:
704 elif v is None:
705 return []
705 return []
706 return v
706 return v
707
707
708 def configdate(self, section, name, default=_unset, untrusted=False):
708 def configdate(self, section, name, default=_unset, untrusted=False):
709 """parse a configuration element as a tuple of ints
709 """parse a configuration element as a tuple of ints
710
710
711 >>> u = ui(); s = b'foo'
711 >>> u = ui(); s = b'foo'
712 >>> u.setconfig(s, b'date', b'0 0')
712 >>> u.setconfig(s, b'date', b'0 0')
713 >>> u.configdate(s, b'date')
713 >>> u.configdate(s, b'date')
714 (0, 0)
714 (0, 0)
715 """
715 """
716 if self.config(section, name, default, untrusted):
716 if self.config(section, name, default, untrusted):
717 return self.configwith(util.parsedate, section, name, default,
717 return self.configwith(util.parsedate, section, name, default,
718 'date', untrusted)
718 'date', untrusted)
719 if default is _unset:
719 if default is _unset:
720 return None
720 return None
721 return default
721 return default
722
722
723 def hasconfig(self, section, name, untrusted=False):
723 def hasconfig(self, section, name, untrusted=False):
724 return self._data(untrusted).hasitem(section, name)
724 return self._data(untrusted).hasitem(section, name)
725
725
726 def has_section(self, section, untrusted=False):
726 def has_section(self, section, untrusted=False):
727 '''tell whether section exists in config.'''
727 '''tell whether section exists in config.'''
728 return section in self._data(untrusted)
728 return section in self._data(untrusted)
729
729
730 def configitems(self, section, untrusted=False, ignoresub=False):
730 def configitems(self, section, untrusted=False, ignoresub=False):
731 items = self._data(untrusted).items(section)
731 items = self._data(untrusted).items(section)
732 if ignoresub:
732 if ignoresub:
733 newitems = {}
733 newitems = {}
734 for k, v in items:
734 for k, v in items:
735 if ':' not in k:
735 if ':' not in k:
736 newitems[k] = v
736 newitems[k] = v
737 items = newitems.items()
737 items = newitems.items()
738 if self.debugflag and not untrusted and self._reportuntrusted:
738 if self.debugflag and not untrusted and self._reportuntrusted:
739 for k, v in self._ucfg.items(section):
739 for k, v in self._ucfg.items(section):
740 if self._tcfg.get(section, k) != v:
740 if self._tcfg.get(section, k) != v:
741 self.debug("ignoring untrusted configuration option "
741 self.debug("ignoring untrusted configuration option "
742 "%s.%s = %s\n" % (section, k, v))
742 "%s.%s = %s\n" % (section, k, v))
743 return items
743 return items
744
744
745 def walkconfig(self, untrusted=False):
745 def walkconfig(self, untrusted=False):
746 cfg = self._data(untrusted)
746 cfg = self._data(untrusted)
747 for section in cfg.sections():
747 for section in cfg.sections():
748 for name, value in self.configitems(section, untrusted):
748 for name, value in self.configitems(section, untrusted):
749 yield section, name, value
749 yield section, name, value
750
750
751 def plain(self, feature=None):
751 def plain(self, feature=None):
752 '''is plain mode active?
752 '''is plain mode active?
753
753
754 Plain mode means that all configuration variables which affect
754 Plain mode means that all configuration variables which affect
755 the behavior and output of Mercurial should be
755 the behavior and output of Mercurial should be
756 ignored. Additionally, the output should be stable,
756 ignored. Additionally, the output should be stable,
757 reproducible and suitable for use in scripts or applications.
757 reproducible and suitable for use in scripts or applications.
758
758
759 The only way to trigger plain mode is by setting either the
759 The only way to trigger plain mode is by setting either the
760 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
760 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
761
761
762 The return value can either be
762 The return value can either be
763 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
763 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
764 - False if feature is disabled by default and not included in HGPLAIN
764 - False if feature is disabled by default and not included in HGPLAIN
765 - True otherwise
765 - True otherwise
766 '''
766 '''
767 if ('HGPLAIN' not in encoding.environ and
767 if ('HGPLAIN' not in encoding.environ and
768 'HGPLAINEXCEPT' not in encoding.environ):
768 'HGPLAINEXCEPT' not in encoding.environ):
769 return False
769 return False
770 exceptions = encoding.environ.get('HGPLAINEXCEPT',
770 exceptions = encoding.environ.get('HGPLAINEXCEPT',
771 '').strip().split(',')
771 '').strip().split(',')
772 # TODO: add support for HGPLAIN=+feature,-feature syntax
772 # TODO: add support for HGPLAIN=+feature,-feature syntax
773 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
773 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
774 exceptions.append('strictflags')
774 exceptions.append('strictflags')
775 if feature and exceptions:
775 if feature and exceptions:
776 return feature not in exceptions
776 return feature not in exceptions
777 return True
777 return True
778
778
779 def username(self, acceptempty=False):
779 def username(self, acceptempty=False):
780 """Return default username to be used in commits.
780 """Return default username to be used in commits.
781
781
782 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
782 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
783 and stop searching if one of these is set.
783 and stop searching if one of these is set.
784 If not found and acceptempty is True, returns None.
784 If not found and acceptempty is True, returns None.
785 If not found and ui.askusername is True, ask the user, else use
785 If not found and ui.askusername is True, ask the user, else use
786 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
786 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
787 If no username could be found, raise an Abort error.
787 If no username could be found, raise an Abort error.
788 """
788 """
789 user = encoding.environ.get("HGUSER")
789 user = encoding.environ.get("HGUSER")
790 if user is None:
790 if user is None:
791 user = self.config("ui", "username")
791 user = self.config("ui", "username")
792 if user is not None:
792 if user is not None:
793 user = os.path.expandvars(user)
793 user = os.path.expandvars(user)
794 if user is None:
794 if user is None:
795 user = encoding.environ.get("EMAIL")
795 user = encoding.environ.get("EMAIL")
796 if user is None and acceptempty:
796 if user is None and acceptempty:
797 return user
797 return user
798 if user is None and self.configbool("ui", "askusername"):
798 if user is None and self.configbool("ui", "askusername"):
799 user = self.prompt(_("enter a commit username:"), default=None)
799 user = self.prompt(_("enter a commit username:"), default=None)
800 if user is None and not self.interactive():
800 if user is None and not self.interactive():
801 try:
801 try:
802 user = '%s@%s' % (util.getuser(), socket.getfqdn())
802 user = '%s@%s' % (util.getuser(), socket.getfqdn())
803 self.warn(_("no username found, using '%s' instead\n") % user)
803 self.warn(_("no username found, using '%s' instead\n") % user)
804 except KeyError:
804 except KeyError:
805 pass
805 pass
806 if not user:
806 if not user:
807 raise error.Abort(_('no username supplied'),
807 raise error.Abort(_('no username supplied'),
808 hint=_("use 'hg config --edit' "
808 hint=_("use 'hg config --edit' "
809 'to set your username'))
809 'to set your username'))
810 if "\n" in user:
810 if "\n" in user:
811 raise error.Abort(_("username %s contains a newline\n")
811 raise error.Abort(_("username %s contains a newline\n")
812 % repr(user))
812 % repr(user))
813 return user
813 return user
814
814
815 def shortuser(self, user):
815 def shortuser(self, user):
816 """Return a short representation of a user name or email address."""
816 """Return a short representation of a user name or email address."""
817 if not self.verbose:
817 if not self.verbose:
818 user = util.shortuser(user)
818 user = util.shortuser(user)
819 return user
819 return user
820
820
821 def expandpath(self, loc, default=None):
821 def expandpath(self, loc, default=None):
822 """Return repository location relative to cwd or from [paths]"""
822 """Return repository location relative to cwd or from [paths]"""
823 try:
823 try:
824 p = self.paths.getpath(loc)
824 p = self.paths.getpath(loc)
825 if p:
825 if p:
826 return p.rawloc
826 return p.rawloc
827 except error.RepoError:
827 except error.RepoError:
828 pass
828 pass
829
829
830 if default:
830 if default:
831 try:
831 try:
832 p = self.paths.getpath(default)
832 p = self.paths.getpath(default)
833 if p:
833 if p:
834 return p.rawloc
834 return p.rawloc
835 except error.RepoError:
835 except error.RepoError:
836 pass
836 pass
837
837
838 return loc
838 return loc
839
839
840 @util.propertycache
840 @util.propertycache
841 def paths(self):
841 def paths(self):
842 return paths(self)
842 return paths(self)
843
843
844 def pushbuffer(self, error=False, subproc=False, labeled=False):
844 def pushbuffer(self, error=False, subproc=False, labeled=False):
845 """install a buffer to capture standard output of the ui object
845 """install a buffer to capture standard output of the ui object
846
846
847 If error is True, the error output will be captured too.
847 If error is True, the error output will be captured too.
848
848
849 If subproc is True, output from subprocesses (typically hooks) will be
849 If subproc is True, output from subprocesses (typically hooks) will be
850 captured too.
850 captured too.
851
851
852 If labeled is True, any labels associated with buffered
852 If labeled is True, any labels associated with buffered
853 output will be handled. By default, this has no effect
853 output will be handled. By default, this has no effect
854 on the output returned, but extensions and GUI tools may
854 on the output returned, but extensions and GUI tools may
855 handle this argument and returned styled output. If output
855 handle this argument and returned styled output. If output
856 is being buffered so it can be captured and parsed or
856 is being buffered so it can be captured and parsed or
857 processed, labeled should not be set to True.
857 processed, labeled should not be set to True.
858 """
858 """
859 self._buffers.append([])
859 self._buffers.append([])
860 self._bufferstates.append((error, subproc, labeled))
860 self._bufferstates.append((error, subproc, labeled))
861 self._bufferapplylabels = labeled
861 self._bufferapplylabels = labeled
862
862
863 def popbuffer(self):
863 def popbuffer(self):
864 '''pop the last buffer and return the buffered output'''
864 '''pop the last buffer and return the buffered output'''
865 self._bufferstates.pop()
865 self._bufferstates.pop()
866 if self._bufferstates:
866 if self._bufferstates:
867 self._bufferapplylabels = self._bufferstates[-1][2]
867 self._bufferapplylabels = self._bufferstates[-1][2]
868 else:
868 else:
869 self._bufferapplylabels = None
869 self._bufferapplylabels = None
870
870
871 return "".join(self._buffers.pop())
871 return "".join(self._buffers.pop())
872
872
873 def write(self, *args, **opts):
873 def write(self, *args, **opts):
874 '''write args to output
874 '''write args to output
875
875
876 By default, this method simply writes to the buffer or stdout.
876 By default, this method simply writes to the buffer or stdout.
877 Color mode can be set on the UI class to have the output decorated
877 Color mode can be set on the UI class to have the output decorated
878 with color modifier before being written to stdout.
878 with color modifier before being written to stdout.
879
879
880 The color used is controlled by an optional keyword argument, "label".
880 The color used is controlled by an optional keyword argument, "label".
881 This should be a string containing label names separated by space.
881 This should be a string containing label names separated by space.
882 Label names take the form of "topic.type". For example, ui.debug()
882 Label names take the form of "topic.type". For example, ui.debug()
883 issues a label of "ui.debug".
883 issues a label of "ui.debug".
884
884
885 When labeling output for a specific command, a label of
885 When labeling output for a specific command, a label of
886 "cmdname.type" is recommended. For example, status issues
886 "cmdname.type" is recommended. For example, status issues
887 a label of "status.modified" for modified files.
887 a label of "status.modified" for modified files.
888 '''
888 '''
889 if self._buffers and not opts.get(r'prompt', False):
889 if self._buffers and not opts.get(r'prompt', False):
890 if self._bufferapplylabels:
890 if self._bufferapplylabels:
891 label = opts.get(r'label', '')
891 label = opts.get(r'label', '')
892 self._buffers[-1].extend(self.label(a, label) for a in args)
892 self._buffers[-1].extend(self.label(a, label) for a in args)
893 else:
893 else:
894 self._buffers[-1].extend(args)
894 self._buffers[-1].extend(args)
895 elif self._colormode == 'win32':
895 elif self._colormode == 'win32':
896 # windows color printing is its own can of crab, defer to
896 # windows color printing is its own can of crab, defer to
897 # the color module and that is it.
897 # the color module and that is it.
898 color.win32print(self, self._write, *args, **opts)
898 color.win32print(self, self._write, *args, **opts)
899 else:
899 else:
900 msgs = args
900 msgs = args
901 if self._colormode is not None:
901 if self._colormode is not None:
902 label = opts.get(r'label', '')
902 label = opts.get(r'label', '')
903 msgs = [self.label(a, label) for a in args]
903 msgs = [self.label(a, label) for a in args]
904 self._write(*msgs, **opts)
904 self._write(*msgs, **opts)
905
905
906 def _write(self, *msgs, **opts):
906 def _write(self, *msgs, **opts):
907 self._progclear()
907 self._progclear()
908 # opencode timeblockedsection because this is a critical path
908 # opencode timeblockedsection because this is a critical path
909 starttime = util.timer()
909 starttime = util.timer()
910 try:
910 try:
911 self.fout.write(''.join(msgs))
911 self.fout.write(''.join(msgs))
912 except IOError as err:
912 except IOError as err:
913 raise error.StdioError(err)
913 raise error.StdioError(err)
914 finally:
914 finally:
915 self._blockedtimes['stdio_blocked'] += \
915 self._blockedtimes['stdio_blocked'] += \
916 (util.timer() - starttime) * 1000
916 (util.timer() - starttime) * 1000
917
917
918 def write_err(self, *args, **opts):
918 def write_err(self, *args, **opts):
919 self._progclear()
919 self._progclear()
920 if self._bufferstates and self._bufferstates[-1][0]:
920 if self._bufferstates and self._bufferstates[-1][0]:
921 self.write(*args, **opts)
921 self.write(*args, **opts)
922 elif self._colormode == 'win32':
922 elif self._colormode == 'win32':
923 # windows color printing is its own can of crab, defer to
923 # windows color printing is its own can of crab, defer to
924 # the color module and that is it.
924 # the color module and that is it.
925 color.win32print(self, self._write_err, *args, **opts)
925 color.win32print(self, self._write_err, *args, **opts)
926 else:
926 else:
927 msgs = args
927 msgs = args
928 if self._colormode is not None:
928 if self._colormode is not None:
929 label = opts.get(r'label', '')
929 label = opts.get(r'label', '')
930 msgs = [self.label(a, label) for a in args]
930 msgs = [self.label(a, label) for a in args]
931 self._write_err(*msgs, **opts)
931 self._write_err(*msgs, **opts)
932
932
933 def _write_err(self, *msgs, **opts):
933 def _write_err(self, *msgs, **opts):
934 try:
934 try:
935 with self.timeblockedsection('stdio'):
935 with self.timeblockedsection('stdio'):
936 if not getattr(self.fout, 'closed', False):
936 if not getattr(self.fout, 'closed', False):
937 self.fout.flush()
937 self.fout.flush()
938 for a in msgs:
938 for a in msgs:
939 self.ferr.write(a)
939 self.ferr.write(a)
940 # stderr may be buffered under win32 when redirected to files,
940 # stderr may be buffered under win32 when redirected to files,
941 # including stdout.
941 # including stdout.
942 if not getattr(self.ferr, 'closed', False):
942 if not getattr(self.ferr, 'closed', False):
943 self.ferr.flush()
943 self.ferr.flush()
944 except IOError as inst:
944 except IOError as inst:
945 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
945 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
946 raise error.StdioError(inst)
946 raise error.StdioError(inst)
947
947
948 def flush(self):
948 def flush(self):
949 # opencode timeblockedsection because this is a critical path
949 # opencode timeblockedsection because this is a critical path
950 starttime = util.timer()
950 starttime = util.timer()
951 try:
951 try:
952 try:
952 try:
953 self.fout.flush()
953 self.fout.flush()
954 except IOError as err:
954 except IOError as err:
955 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
955 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
956 raise error.StdioError(err)
956 raise error.StdioError(err)
957 finally:
957 finally:
958 try:
958 try:
959 self.ferr.flush()
959 self.ferr.flush()
960 except IOError as err:
960 except IOError as err:
961 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
961 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
962 raise error.StdioError(err)
962 raise error.StdioError(err)
963 finally:
963 finally:
964 self._blockedtimes['stdio_blocked'] += \
964 self._blockedtimes['stdio_blocked'] += \
965 (util.timer() - starttime) * 1000
965 (util.timer() - starttime) * 1000
966
966
967 def _isatty(self, fh):
967 def _isatty(self, fh):
968 if self.configbool('ui', 'nontty'):
968 if self.configbool('ui', 'nontty'):
969 return False
969 return False
970 return util.isatty(fh)
970 return util.isatty(fh)
971
971
972 def disablepager(self):
972 def disablepager(self):
973 self._disablepager = True
973 self._disablepager = True
974
974
975 def pager(self, command):
975 def pager(self, command):
976 """Start a pager for subsequent command output.
976 """Start a pager for subsequent command output.
977
977
978 Commands which produce a long stream of output should call
978 Commands which produce a long stream of output should call
979 this function to activate the user's preferred pagination
979 this function to activate the user's preferred pagination
980 mechanism (which may be no pager). Calling this function
980 mechanism (which may be no pager). Calling this function
981 precludes any future use of interactive functionality, such as
981 precludes any future use of interactive functionality, such as
982 prompting the user or activating curses.
982 prompting the user or activating curses.
983
983
984 Args:
984 Args:
985 command: The full, non-aliased name of the command. That is, "log"
985 command: The full, non-aliased name of the command. That is, "log"
986 not "history, "summary" not "summ", etc.
986 not "history, "summary" not "summ", etc.
987 """
987 """
988 if (self._disablepager
988 if (self._disablepager
989 or self.pageractive):
989 or self.pageractive):
990 # how pager should do is already determined
990 # how pager should do is already determined
991 return
991 return
992
992
993 if not command.startswith('internal-always-') and (
993 if not command.startswith('internal-always-') and (
994 # explicit --pager=on (= 'internal-always-' prefix) should
994 # explicit --pager=on (= 'internal-always-' prefix) should
995 # take precedence over disabling factors below
995 # take precedence over disabling factors below
996 command in self.configlist('pager', 'ignore')
996 command in self.configlist('pager', 'ignore')
997 or not self.configbool('ui', 'paginate')
997 or not self.configbool('ui', 'paginate')
998 or not self.configbool('pager', 'attend-' + command, True)
998 or not self.configbool('pager', 'attend-' + command, True)
999 # TODO: if we want to allow HGPLAINEXCEPT=pager,
999 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1000 # formatted() will need some adjustment.
1000 # formatted() will need some adjustment.
1001 or not self.formatted()
1001 or not self.formatted()
1002 or self.plain()
1002 or self.plain()
1003 or self._buffers
1003 or self._buffers
1004 # TODO: expose debugger-enabled on the UI object
1004 # TODO: expose debugger-enabled on the UI object
1005 or '--debugger' in pycompat.sysargv):
1005 or '--debugger' in pycompat.sysargv):
1006 # We only want to paginate if the ui appears to be
1006 # We only want to paginate if the ui appears to be
1007 # interactive, the user didn't say HGPLAIN or
1007 # interactive, the user didn't say HGPLAIN or
1008 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1008 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1009 return
1009 return
1010
1010
1011 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1011 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1012 if not pagercmd:
1012 if not pagercmd:
1013 return
1013 return
1014
1014
1015 pagerenv = {}
1015 pagerenv = {}
1016 for name, value in rcutil.defaultpagerenv().items():
1016 for name, value in rcutil.defaultpagerenv().items():
1017 if name not in encoding.environ:
1017 if name not in encoding.environ:
1018 pagerenv[name] = value
1018 pagerenv[name] = value
1019
1019
1020 self.debug('starting pager for command %r\n' % command)
1020 self.debug('starting pager for command %r\n' % command)
1021 self.flush()
1021 self.flush()
1022
1022
1023 wasformatted = self.formatted()
1023 wasformatted = self.formatted()
1024 if util.safehasattr(signal, "SIGPIPE"):
1024 if util.safehasattr(signal, "SIGPIPE"):
1025 signal.signal(signal.SIGPIPE, _catchterm)
1025 signal.signal(signal.SIGPIPE, _catchterm)
1026 if self._runpager(pagercmd, pagerenv):
1026 if self._runpager(pagercmd, pagerenv):
1027 self.pageractive = True
1027 self.pageractive = True
1028 # Preserve the formatted-ness of the UI. This is important
1028 # Preserve the formatted-ness of the UI. This is important
1029 # because we mess with stdout, which might confuse
1029 # because we mess with stdout, which might confuse
1030 # auto-detection of things being formatted.
1030 # auto-detection of things being formatted.
1031 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1031 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1032 self.setconfig('ui', 'interactive', False, 'pager')
1032 self.setconfig('ui', 'interactive', False, 'pager')
1033
1033
1034 # If pagermode differs from color.mode, reconfigure color now that
1034 # If pagermode differs from color.mode, reconfigure color now that
1035 # pageractive is set.
1035 # pageractive is set.
1036 cm = self._colormode
1036 cm = self._colormode
1037 if cm != self.config('color', 'pagermode', cm):
1037 if cm != self.config('color', 'pagermode', cm):
1038 color.setup(self)
1038 color.setup(self)
1039 else:
1039 else:
1040 # If the pager can't be spawned in dispatch when --pager=on is
1040 # If the pager can't be spawned in dispatch when --pager=on is
1041 # given, don't try again when the command runs, to avoid a duplicate
1041 # given, don't try again when the command runs, to avoid a duplicate
1042 # warning about a missing pager command.
1042 # warning about a missing pager command.
1043 self.disablepager()
1043 self.disablepager()
1044
1044
1045 def _runpager(self, command, env=None):
1045 def _runpager(self, command, env=None):
1046 """Actually start the pager and set up file descriptors.
1046 """Actually start the pager and set up file descriptors.
1047
1047
1048 This is separate in part so that extensions (like chg) can
1048 This is separate in part so that extensions (like chg) can
1049 override how a pager is invoked.
1049 override how a pager is invoked.
1050 """
1050 """
1051 if command == 'cat':
1051 if command == 'cat':
1052 # Save ourselves some work.
1052 # Save ourselves some work.
1053 return False
1053 return False
1054 # If the command doesn't contain any of these characters, we
1054 # If the command doesn't contain any of these characters, we
1055 # assume it's a binary and exec it directly. This means for
1055 # assume it's a binary and exec it directly. This means for
1056 # simple pager command configurations, we can degrade
1056 # simple pager command configurations, we can degrade
1057 # gracefully and tell the user about their broken pager.
1057 # gracefully and tell the user about their broken pager.
1058 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1058 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1059
1059
1060 if pycompat.iswindows and not shell:
1060 if pycompat.iswindows and not shell:
1061 # Window's built-in `more` cannot be invoked with shell=False, but
1061 # Window's built-in `more` cannot be invoked with shell=False, but
1062 # its `more.com` can. Hide this implementation detail from the
1062 # its `more.com` can. Hide this implementation detail from the
1063 # user so we can also get sane bad PAGER behavior. MSYS has
1063 # user so we can also get sane bad PAGER behavior. MSYS has
1064 # `more.exe`, so do a cmd.exe style resolution of the executable to
1064 # `more.exe`, so do a cmd.exe style resolution of the executable to
1065 # determine which one to use.
1065 # determine which one to use.
1066 fullcmd = util.findexe(command)
1066 fullcmd = util.findexe(command)
1067 if not fullcmd:
1067 if not fullcmd:
1068 self.warn(_("missing pager command '%s', skipping pager\n")
1068 self.warn(_("missing pager command '%s', skipping pager\n")
1069 % command)
1069 % command)
1070 return False
1070 return False
1071
1071
1072 command = fullcmd
1072 command = fullcmd
1073
1073
1074 try:
1074 try:
1075 pager = subprocess.Popen(
1075 pager = subprocess.Popen(
1076 command, shell=shell, bufsize=-1,
1076 command, shell=shell, bufsize=-1,
1077 close_fds=util.closefds, stdin=subprocess.PIPE,
1077 close_fds=util.closefds, stdin=subprocess.PIPE,
1078 stdout=util.stdout, stderr=util.stderr,
1078 stdout=util.stdout, stderr=util.stderr,
1079 env=util.shellenviron(env))
1079 env=util.shellenviron(env))
1080 except OSError as e:
1080 except OSError as e:
1081 if e.errno == errno.ENOENT and not shell:
1081 if e.errno == errno.ENOENT and not shell:
1082 self.warn(_("missing pager command '%s', skipping pager\n")
1082 self.warn(_("missing pager command '%s', skipping pager\n")
1083 % command)
1083 % command)
1084 return False
1084 return False
1085 raise
1085 raise
1086
1086
1087 # back up original file descriptors
1087 # back up original file descriptors
1088 stdoutfd = os.dup(util.stdout.fileno())
1088 stdoutfd = os.dup(util.stdout.fileno())
1089 stderrfd = os.dup(util.stderr.fileno())
1089 stderrfd = os.dup(util.stderr.fileno())
1090
1090
1091 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1091 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1092 if self._isatty(util.stderr):
1092 if self._isatty(util.stderr):
1093 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1093 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1094
1094
1095 @self.atexit
1095 @self.atexit
1096 def killpager():
1096 def killpager():
1097 if util.safehasattr(signal, "SIGINT"):
1097 if util.safehasattr(signal, "SIGINT"):
1098 signal.signal(signal.SIGINT, signal.SIG_IGN)
1098 signal.signal(signal.SIGINT, signal.SIG_IGN)
1099 # restore original fds, closing pager.stdin copies in the process
1099 # restore original fds, closing pager.stdin copies in the process
1100 os.dup2(stdoutfd, util.stdout.fileno())
1100 os.dup2(stdoutfd, util.stdout.fileno())
1101 os.dup2(stderrfd, util.stderr.fileno())
1101 os.dup2(stderrfd, util.stderr.fileno())
1102 pager.stdin.close()
1102 pager.stdin.close()
1103 pager.wait()
1103 pager.wait()
1104
1104
1105 return True
1105 return True
1106
1106
1107 @property
1107 @property
1108 def _exithandlers(self):
1108 def _exithandlers(self):
1109 return _reqexithandlers
1109 return _reqexithandlers
1110
1110
1111 def atexit(self, func, *args, **kwargs):
1111 def atexit(self, func, *args, **kwargs):
1112 '''register a function to run after dispatching a request
1112 '''register a function to run after dispatching a request
1113
1113
1114 Handlers do not stay registered across request boundaries.'''
1114 Handlers do not stay registered across request boundaries.'''
1115 self._exithandlers.append((func, args, kwargs))
1115 self._exithandlers.append((func, args, kwargs))
1116 return func
1116 return func
1117
1117
1118 def interface(self, feature):
1118 def interface(self, feature):
1119 """what interface to use for interactive console features?
1119 """what interface to use for interactive console features?
1120
1120
1121 The interface is controlled by the value of `ui.interface` but also by
1121 The interface is controlled by the value of `ui.interface` but also by
1122 the value of feature-specific configuration. For example:
1122 the value of feature-specific configuration. For example:
1123
1123
1124 ui.interface.histedit = text
1124 ui.interface.histedit = text
1125 ui.interface.chunkselector = curses
1125 ui.interface.chunkselector = curses
1126
1126
1127 Here the features are "histedit" and "chunkselector".
1127 Here the features are "histedit" and "chunkselector".
1128
1128
1129 The configuration above means that the default interfaces for commands
1129 The configuration above means that the default interfaces for commands
1130 is curses, the interface for histedit is text and the interface for
1130 is curses, the interface for histedit is text and the interface for
1131 selecting chunk is crecord (the best curses interface available).
1131 selecting chunk is crecord (the best curses interface available).
1132
1132
1133 Consider the following example:
1133 Consider the following example:
1134 ui.interface = curses
1134 ui.interface = curses
1135 ui.interface.histedit = text
1135 ui.interface.histedit = text
1136
1136
1137 Then histedit will use the text interface and chunkselector will use
1137 Then histedit will use the text interface and chunkselector will use
1138 the default curses interface (crecord at the moment).
1138 the default curses interface (crecord at the moment).
1139 """
1139 """
1140 alldefaults = frozenset(["text", "curses"])
1140 alldefaults = frozenset(["text", "curses"])
1141
1141
1142 featureinterfaces = {
1142 featureinterfaces = {
1143 "chunkselector": [
1143 "chunkselector": [
1144 "text",
1144 "text",
1145 "curses",
1145 "curses",
1146 ]
1146 ]
1147 }
1147 }
1148
1148
1149 # Feature-specific interface
1149 # Feature-specific interface
1150 if feature not in featureinterfaces.keys():
1150 if feature not in featureinterfaces.keys():
1151 # Programming error, not user error
1151 # Programming error, not user error
1152 raise ValueError("Unknown feature requested %s" % feature)
1152 raise ValueError("Unknown feature requested %s" % feature)
1153
1153
1154 availableinterfaces = frozenset(featureinterfaces[feature])
1154 availableinterfaces = frozenset(featureinterfaces[feature])
1155 if alldefaults > availableinterfaces:
1155 if alldefaults > availableinterfaces:
1156 # Programming error, not user error. We need a use case to
1156 # Programming error, not user error. We need a use case to
1157 # define the right thing to do here.
1157 # define the right thing to do here.
1158 raise ValueError(
1158 raise ValueError(
1159 "Feature %s does not handle all default interfaces" %
1159 "Feature %s does not handle all default interfaces" %
1160 feature)
1160 feature)
1161
1161
1162 if self.plain():
1162 if self.plain():
1163 return "text"
1163 return "text"
1164
1164
1165 # Default interface for all the features
1165 # Default interface for all the features
1166 defaultinterface = "text"
1166 defaultinterface = "text"
1167 i = self.config("ui", "interface")
1167 i = self.config("ui", "interface")
1168 if i in alldefaults:
1168 if i in alldefaults:
1169 defaultinterface = i
1169 defaultinterface = i
1170
1170
1171 choseninterface = defaultinterface
1171 choseninterface = defaultinterface
1172 f = self.config("ui", "interface.%s" % feature)
1172 f = self.config("ui", "interface.%s" % feature)
1173 if f in availableinterfaces:
1173 if f in availableinterfaces:
1174 choseninterface = f
1174 choseninterface = f
1175
1175
1176 if i is not None and defaultinterface != i:
1176 if i is not None and defaultinterface != i:
1177 if f is not None:
1177 if f is not None:
1178 self.warn(_("invalid value for ui.interface: %s\n") %
1178 self.warn(_("invalid value for ui.interface: %s\n") %
1179 (i,))
1179 (i,))
1180 else:
1180 else:
1181 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1181 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1182 (i, choseninterface))
1182 (i, choseninterface))
1183 if f is not None and choseninterface != f:
1183 if f is not None and choseninterface != f:
1184 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1184 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1185 (feature, f, choseninterface))
1185 (feature, f, choseninterface))
1186
1186
1187 return choseninterface
1187 return choseninterface
1188
1188
1189 def interactive(self):
1189 def interactive(self):
1190 '''is interactive input allowed?
1190 '''is interactive input allowed?
1191
1191
1192 An interactive session is a session where input can be reasonably read
1192 An interactive session is a session where input can be reasonably read
1193 from `sys.stdin'. If this function returns false, any attempt to read
1193 from `sys.stdin'. If this function returns false, any attempt to read
1194 from stdin should fail with an error, unless a sensible default has been
1194 from stdin should fail with an error, unless a sensible default has been
1195 specified.
1195 specified.
1196
1196
1197 Interactiveness is triggered by the value of the `ui.interactive'
1197 Interactiveness is triggered by the value of the `ui.interactive'
1198 configuration variable or - if it is unset - when `sys.stdin' points
1198 configuration variable or - if it is unset - when `sys.stdin' points
1199 to a terminal device.
1199 to a terminal device.
1200
1200
1201 This function refers to input only; for output, see `ui.formatted()'.
1201 This function refers to input only; for output, see `ui.formatted()'.
1202 '''
1202 '''
1203 i = self.configbool("ui", "interactive")
1203 i = self.configbool("ui", "interactive")
1204 if i is None:
1204 if i is None:
1205 # some environments replace stdin without implementing isatty
1205 # some environments replace stdin without implementing isatty
1206 # usually those are non-interactive
1206 # usually those are non-interactive
1207 return self._isatty(self.fin)
1207 return self._isatty(self.fin)
1208
1208
1209 return i
1209 return i
1210
1210
1211 def termwidth(self):
1211 def termwidth(self):
1212 '''how wide is the terminal in columns?
1212 '''how wide is the terminal in columns?
1213 '''
1213 '''
1214 if 'COLUMNS' in encoding.environ:
1214 if 'COLUMNS' in encoding.environ:
1215 try:
1215 try:
1216 return int(encoding.environ['COLUMNS'])
1216 return int(encoding.environ['COLUMNS'])
1217 except ValueError:
1217 except ValueError:
1218 pass
1218 pass
1219 return scmutil.termsize(self)[0]
1219 return scmutil.termsize(self)[0]
1220
1220
1221 def formatted(self):
1221 def formatted(self):
1222 '''should formatted output be used?
1222 '''should formatted output be used?
1223
1223
1224 It is often desirable to format the output to suite the output medium.
1224 It is often desirable to format the output to suite the output medium.
1225 Examples of this are truncating long lines or colorizing messages.
1225 Examples of this are truncating long lines or colorizing messages.
1226 However, this is not often not desirable when piping output into other
1226 However, this is not often not desirable when piping output into other
1227 utilities, e.g. `grep'.
1227 utilities, e.g. `grep'.
1228
1228
1229 Formatted output is triggered by the value of the `ui.formatted'
1229 Formatted output is triggered by the value of the `ui.formatted'
1230 configuration variable or - if it is unset - when `sys.stdout' points
1230 configuration variable or - if it is unset - when `sys.stdout' points
1231 to a terminal device. Please note that `ui.formatted' should be
1231 to a terminal device. Please note that `ui.formatted' should be
1232 considered an implementation detail; it is not intended for use outside
1232 considered an implementation detail; it is not intended for use outside
1233 Mercurial or its extensions.
1233 Mercurial or its extensions.
1234
1234
1235 This function refers to output only; for input, see `ui.interactive()'.
1235 This function refers to output only; for input, see `ui.interactive()'.
1236 This function always returns false when in plain mode, see `ui.plain()'.
1236 This function always returns false when in plain mode, see `ui.plain()'.
1237 '''
1237 '''
1238 if self.plain():
1238 if self.plain():
1239 return False
1239 return False
1240
1240
1241 i = self.configbool("ui", "formatted")
1241 i = self.configbool("ui", "formatted")
1242 if i is None:
1242 if i is None:
1243 # some environments replace stdout without implementing isatty
1243 # some environments replace stdout without implementing isatty
1244 # usually those are non-interactive
1244 # usually those are non-interactive
1245 return self._isatty(self.fout)
1245 return self._isatty(self.fout)
1246
1246
1247 return i
1247 return i
1248
1248
1249 def _readline(self, prompt=''):
1249 def _readline(self):
1250 if self._isatty(self.fin):
1250 if self._isatty(self.fin):
1251 try:
1251 try:
1252 # magically add command line editing support, where
1252 # magically add command line editing support, where
1253 # available
1253 # available
1254 import readline
1254 import readline
1255 # force demandimport to really load the module
1255 # force demandimport to really load the module
1256 readline.read_history_file
1256 readline.read_history_file
1257 # windows sometimes raises something other than ImportError
1257 # windows sometimes raises something other than ImportError
1258 except Exception:
1258 except Exception:
1259 pass
1259 pass
1260
1260
1261 # call write() so output goes through subclassed implementation
1262 # e.g. color extension on Windows
1263 self.write(prompt, prompt=True)
1264 self.flush()
1265
1266 # prompt ' ' must exist; otherwise readline may delete entire line
1261 # prompt ' ' must exist; otherwise readline may delete entire line
1267 # - http://bugs.python.org/issue12833
1262 # - http://bugs.python.org/issue12833
1268 with self.timeblockedsection('stdio'):
1263 with self.timeblockedsection('stdio'):
1269 line = util.bytesinput(self.fin, self.fout, r' ')
1264 line = util.bytesinput(self.fin, self.fout, r' ')
1270
1265
1271 # When stdin is in binary mode on Windows, it can cause
1266 # When stdin is in binary mode on Windows, it can cause
1272 # raw_input() to emit an extra trailing carriage return
1267 # raw_input() to emit an extra trailing carriage return
1273 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1268 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1274 line = line[:-1]
1269 line = line[:-1]
1275 return line
1270 return line
1276
1271
1277 def prompt(self, msg, default="y"):
1272 def prompt(self, msg, default="y"):
1278 """Prompt user with msg, read response.
1273 """Prompt user with msg, read response.
1279 If ui is not interactive, the default is returned.
1274 If ui is not interactive, the default is returned.
1280 """
1275 """
1281 if not self.interactive():
1276 if not self.interactive():
1282 self.write(msg, ' ', default or '', "\n")
1277 self.write(msg, ' ', default or '', "\n")
1283 return default
1278 return default
1279 self.write(msg, label='ui.prompt', prompt=True)
1280 self.flush()
1284 try:
1281 try:
1285 r = self._readline(self.label(msg, 'ui.prompt'))
1282 r = self._readline()
1286 if not r:
1283 if not r:
1287 r = default
1284 r = default
1288 if self.configbool('ui', 'promptecho'):
1285 if self.configbool('ui', 'promptecho'):
1289 self.write(r, "\n")
1286 self.write(r, "\n")
1290 return r
1287 return r
1291 except EOFError:
1288 except EOFError:
1292 raise error.ResponseExpected()
1289 raise error.ResponseExpected()
1293
1290
1294 @staticmethod
1291 @staticmethod
1295 def extractchoices(prompt):
1292 def extractchoices(prompt):
1296 """Extract prompt message and list of choices from specified prompt.
1293 """Extract prompt message and list of choices from specified prompt.
1297
1294
1298 This returns tuple "(message, choices)", and "choices" is the
1295 This returns tuple "(message, choices)", and "choices" is the
1299 list of tuple "(response character, text without &)".
1296 list of tuple "(response character, text without &)".
1300
1297
1301 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1298 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1302 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1299 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1303 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1300 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1304 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1301 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1305 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1302 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1306 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1303 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1307 """
1304 """
1308
1305
1309 # Sadly, the prompt string may have been built with a filename
1306 # Sadly, the prompt string may have been built with a filename
1310 # containing "$$" so let's try to find the first valid-looking
1307 # containing "$$" so let's try to find the first valid-looking
1311 # prompt to start parsing. Sadly, we also can't rely on
1308 # prompt to start parsing. Sadly, we also can't rely on
1312 # choices containing spaces, ASCII, or basically anything
1309 # choices containing spaces, ASCII, or basically anything
1313 # except an ampersand followed by a character.
1310 # except an ampersand followed by a character.
1314 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1311 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1315 msg = m.group(1)
1312 msg = m.group(1)
1316 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1313 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1317 def choicetuple(s):
1314 def choicetuple(s):
1318 ampidx = s.index('&')
1315 ampidx = s.index('&')
1319 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1316 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1320 return (msg, [choicetuple(s) for s in choices])
1317 return (msg, [choicetuple(s) for s in choices])
1321
1318
1322 def promptchoice(self, prompt, default=0):
1319 def promptchoice(self, prompt, default=0):
1323 """Prompt user with a message, read response, and ensure it matches
1320 """Prompt user with a message, read response, and ensure it matches
1324 one of the provided choices. The prompt is formatted as follows:
1321 one of the provided choices. The prompt is formatted as follows:
1325
1322
1326 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1323 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1327
1324
1328 The index of the choice is returned. Responses are case
1325 The index of the choice is returned. Responses are case
1329 insensitive. If ui is not interactive, the default is
1326 insensitive. If ui is not interactive, the default is
1330 returned.
1327 returned.
1331 """
1328 """
1332
1329
1333 msg, choices = self.extractchoices(prompt)
1330 msg, choices = self.extractchoices(prompt)
1334 resps = [r for r, t in choices]
1331 resps = [r for r, t in choices]
1335 while True:
1332 while True:
1336 r = self.prompt(msg, resps[default])
1333 r = self.prompt(msg, resps[default])
1337 if r.lower() in resps:
1334 if r.lower() in resps:
1338 return resps.index(r.lower())
1335 return resps.index(r.lower())
1339 self.write(_("unrecognized response\n"))
1336 self.write(_("unrecognized response\n"))
1340
1337
1341 def getpass(self, prompt=None, default=None):
1338 def getpass(self, prompt=None, default=None):
1342 if not self.interactive():
1339 if not self.interactive():
1343 return default
1340 return default
1344 try:
1341 try:
1345 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1342 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1346 # disable getpass() only if explicitly specified. it's still valid
1343 # disable getpass() only if explicitly specified. it's still valid
1347 # to interact with tty even if fin is not a tty.
1344 # to interact with tty even if fin is not a tty.
1348 with self.timeblockedsection('stdio'):
1345 with self.timeblockedsection('stdio'):
1349 if self.configbool('ui', 'nontty'):
1346 if self.configbool('ui', 'nontty'):
1350 l = self.fin.readline()
1347 l = self.fin.readline()
1351 if not l:
1348 if not l:
1352 raise EOFError
1349 raise EOFError
1353 return l.rstrip('\n')
1350 return l.rstrip('\n')
1354 else:
1351 else:
1355 return getpass.getpass('')
1352 return getpass.getpass('')
1356 except EOFError:
1353 except EOFError:
1357 raise error.ResponseExpected()
1354 raise error.ResponseExpected()
1358 def status(self, *msg, **opts):
1355 def status(self, *msg, **opts):
1359 '''write status message to output (if ui.quiet is False)
1356 '''write status message to output (if ui.quiet is False)
1360
1357
1361 This adds an output label of "ui.status".
1358 This adds an output label of "ui.status".
1362 '''
1359 '''
1363 if not self.quiet:
1360 if not self.quiet:
1364 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1361 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1365 self.write(*msg, **opts)
1362 self.write(*msg, **opts)
1366 def warn(self, *msg, **opts):
1363 def warn(self, *msg, **opts):
1367 '''write warning message to output (stderr)
1364 '''write warning message to output (stderr)
1368
1365
1369 This adds an output label of "ui.warning".
1366 This adds an output label of "ui.warning".
1370 '''
1367 '''
1371 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1368 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1372 self.write_err(*msg, **opts)
1369 self.write_err(*msg, **opts)
1373 def note(self, *msg, **opts):
1370 def note(self, *msg, **opts):
1374 '''write note to output (if ui.verbose is True)
1371 '''write note to output (if ui.verbose is True)
1375
1372
1376 This adds an output label of "ui.note".
1373 This adds an output label of "ui.note".
1377 '''
1374 '''
1378 if self.verbose:
1375 if self.verbose:
1379 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1376 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1380 self.write(*msg, **opts)
1377 self.write(*msg, **opts)
1381 def debug(self, *msg, **opts):
1378 def debug(self, *msg, **opts):
1382 '''write debug message to output (if ui.debugflag is True)
1379 '''write debug message to output (if ui.debugflag is True)
1383
1380
1384 This adds an output label of "ui.debug".
1381 This adds an output label of "ui.debug".
1385 '''
1382 '''
1386 if self.debugflag:
1383 if self.debugflag:
1387 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1384 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1388 self.write(*msg, **opts)
1385 self.write(*msg, **opts)
1389
1386
1390 def edit(self, text, user, extra=None, editform=None, pending=None,
1387 def edit(self, text, user, extra=None, editform=None, pending=None,
1391 repopath=None, action=None):
1388 repopath=None, action=None):
1392 if action is None:
1389 if action is None:
1393 self.develwarn('action is None but will soon be a required '
1390 self.develwarn('action is None but will soon be a required '
1394 'parameter to ui.edit()')
1391 'parameter to ui.edit()')
1395 extra_defaults = {
1392 extra_defaults = {
1396 'prefix': 'editor',
1393 'prefix': 'editor',
1397 'suffix': '.txt',
1394 'suffix': '.txt',
1398 }
1395 }
1399 if extra is not None:
1396 if extra is not None:
1400 if extra.get('suffix') is not None:
1397 if extra.get('suffix') is not None:
1401 self.develwarn('extra.suffix is not None but will soon be '
1398 self.develwarn('extra.suffix is not None but will soon be '
1402 'ignored by ui.edit()')
1399 'ignored by ui.edit()')
1403 extra_defaults.update(extra)
1400 extra_defaults.update(extra)
1404 extra = extra_defaults
1401 extra = extra_defaults
1405
1402
1406 if action == 'diff':
1403 if action == 'diff':
1407 suffix = '.diff'
1404 suffix = '.diff'
1408 elif action:
1405 elif action:
1409 suffix = '.%s.hg.txt' % action
1406 suffix = '.%s.hg.txt' % action
1410 else:
1407 else:
1411 suffix = extra['suffix']
1408 suffix = extra['suffix']
1412
1409
1413 rdir = None
1410 rdir = None
1414 if self.configbool('experimental', 'editortmpinhg'):
1411 if self.configbool('experimental', 'editortmpinhg'):
1415 rdir = repopath
1412 rdir = repopath
1416 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1413 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1417 suffix=suffix,
1414 suffix=suffix,
1418 dir=rdir)
1415 dir=rdir)
1419 try:
1416 try:
1420 f = os.fdopen(fd, r'wb')
1417 f = os.fdopen(fd, r'wb')
1421 f.write(util.tonativeeol(text))
1418 f.write(util.tonativeeol(text))
1422 f.close()
1419 f.close()
1423
1420
1424 environ = {'HGUSER': user}
1421 environ = {'HGUSER': user}
1425 if 'transplant_source' in extra:
1422 if 'transplant_source' in extra:
1426 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1423 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1427 for label in ('intermediate-source', 'source', 'rebase_source'):
1424 for label in ('intermediate-source', 'source', 'rebase_source'):
1428 if label in extra:
1425 if label in extra:
1429 environ.update({'HGREVISION': extra[label]})
1426 environ.update({'HGREVISION': extra[label]})
1430 break
1427 break
1431 if editform:
1428 if editform:
1432 environ.update({'HGEDITFORM': editform})
1429 environ.update({'HGEDITFORM': editform})
1433 if pending:
1430 if pending:
1434 environ.update({'HG_PENDING': pending})
1431 environ.update({'HG_PENDING': pending})
1435
1432
1436 editor = self.geteditor()
1433 editor = self.geteditor()
1437
1434
1438 self.system("%s \"%s\"" % (editor, name),
1435 self.system("%s \"%s\"" % (editor, name),
1439 environ=environ,
1436 environ=environ,
1440 onerr=error.Abort, errprefix=_("edit failed"),
1437 onerr=error.Abort, errprefix=_("edit failed"),
1441 blockedtag='editor')
1438 blockedtag='editor')
1442
1439
1443 f = open(name, r'rb')
1440 f = open(name, r'rb')
1444 t = util.fromnativeeol(f.read())
1441 t = util.fromnativeeol(f.read())
1445 f.close()
1442 f.close()
1446 finally:
1443 finally:
1447 os.unlink(name)
1444 os.unlink(name)
1448
1445
1449 return t
1446 return t
1450
1447
1451 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1448 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1452 blockedtag=None):
1449 blockedtag=None):
1453 '''execute shell command with appropriate output stream. command
1450 '''execute shell command with appropriate output stream. command
1454 output will be redirected if fout is not stdout.
1451 output will be redirected if fout is not stdout.
1455
1452
1456 if command fails and onerr is None, return status, else raise onerr
1453 if command fails and onerr is None, return status, else raise onerr
1457 object as exception.
1454 object as exception.
1458 '''
1455 '''
1459 if blockedtag is None:
1456 if blockedtag is None:
1460 # Long cmds tend to be because of an absolute path on cmd. Keep
1457 # Long cmds tend to be because of an absolute path on cmd. Keep
1461 # the tail end instead
1458 # the tail end instead
1462 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1459 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1463 blockedtag = 'unknown_system_' + cmdsuffix
1460 blockedtag = 'unknown_system_' + cmdsuffix
1464 out = self.fout
1461 out = self.fout
1465 if any(s[1] for s in self._bufferstates):
1462 if any(s[1] for s in self._bufferstates):
1466 out = self
1463 out = self
1467 with self.timeblockedsection(blockedtag):
1464 with self.timeblockedsection(blockedtag):
1468 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1465 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1469 if rc and onerr:
1466 if rc and onerr:
1470 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1467 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1471 util.explainexit(rc)[0])
1468 util.explainexit(rc)[0])
1472 if errprefix:
1469 if errprefix:
1473 errmsg = '%s: %s' % (errprefix, errmsg)
1470 errmsg = '%s: %s' % (errprefix, errmsg)
1474 raise onerr(errmsg)
1471 raise onerr(errmsg)
1475 return rc
1472 return rc
1476
1473
1477 def _runsystem(self, cmd, environ, cwd, out):
1474 def _runsystem(self, cmd, environ, cwd, out):
1478 """actually execute the given shell command (can be overridden by
1475 """actually execute the given shell command (can be overridden by
1479 extensions like chg)"""
1476 extensions like chg)"""
1480 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1477 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1481
1478
1482 def traceback(self, exc=None, force=False):
1479 def traceback(self, exc=None, force=False):
1483 '''print exception traceback if traceback printing enabled or forced.
1480 '''print exception traceback if traceback printing enabled or forced.
1484 only to call in exception handler. returns true if traceback
1481 only to call in exception handler. returns true if traceback
1485 printed.'''
1482 printed.'''
1486 if self.tracebackflag or force:
1483 if self.tracebackflag or force:
1487 if exc is None:
1484 if exc is None:
1488 exc = sys.exc_info()
1485 exc = sys.exc_info()
1489 cause = getattr(exc[1], 'cause', None)
1486 cause = getattr(exc[1], 'cause', None)
1490
1487
1491 if cause is not None:
1488 if cause is not None:
1492 causetb = traceback.format_tb(cause[2])
1489 causetb = traceback.format_tb(cause[2])
1493 exctb = traceback.format_tb(exc[2])
1490 exctb = traceback.format_tb(exc[2])
1494 exconly = traceback.format_exception_only(cause[0], cause[1])
1491 exconly = traceback.format_exception_only(cause[0], cause[1])
1495
1492
1496 # exclude frame where 'exc' was chained and rethrown from exctb
1493 # exclude frame where 'exc' was chained and rethrown from exctb
1497 self.write_err('Traceback (most recent call last):\n',
1494 self.write_err('Traceback (most recent call last):\n',
1498 ''.join(exctb[:-1]),
1495 ''.join(exctb[:-1]),
1499 ''.join(causetb),
1496 ''.join(causetb),
1500 ''.join(exconly))
1497 ''.join(exconly))
1501 else:
1498 else:
1502 output = traceback.format_exception(exc[0], exc[1], exc[2])
1499 output = traceback.format_exception(exc[0], exc[1], exc[2])
1503 self.write_err(encoding.strtolocal(r''.join(output)))
1500 self.write_err(encoding.strtolocal(r''.join(output)))
1504 return self.tracebackflag or force
1501 return self.tracebackflag or force
1505
1502
1506 def geteditor(self):
1503 def geteditor(self):
1507 '''return editor to use'''
1504 '''return editor to use'''
1508 if pycompat.sysplatform == 'plan9':
1505 if pycompat.sysplatform == 'plan9':
1509 # vi is the MIPS instruction simulator on Plan 9. We
1506 # vi is the MIPS instruction simulator on Plan 9. We
1510 # instead default to E to plumb commit messages to
1507 # instead default to E to plumb commit messages to
1511 # avoid confusion.
1508 # avoid confusion.
1512 editor = 'E'
1509 editor = 'E'
1513 else:
1510 else:
1514 editor = 'vi'
1511 editor = 'vi'
1515 return (encoding.environ.get("HGEDITOR") or
1512 return (encoding.environ.get("HGEDITOR") or
1516 self.config("ui", "editor", editor))
1513 self.config("ui", "editor", editor))
1517
1514
1518 @util.propertycache
1515 @util.propertycache
1519 def _progbar(self):
1516 def _progbar(self):
1520 """setup the progbar singleton to the ui object"""
1517 """setup the progbar singleton to the ui object"""
1521 if (self.quiet or self.debugflag
1518 if (self.quiet or self.debugflag
1522 or self.configbool('progress', 'disable')
1519 or self.configbool('progress', 'disable')
1523 or not progress.shouldprint(self)):
1520 or not progress.shouldprint(self)):
1524 return None
1521 return None
1525 return getprogbar(self)
1522 return getprogbar(self)
1526
1523
1527 def _progclear(self):
1524 def _progclear(self):
1528 """clear progress bar output if any. use it before any output"""
1525 """clear progress bar output if any. use it before any output"""
1529 if not haveprogbar(): # nothing loaded yet
1526 if not haveprogbar(): # nothing loaded yet
1530 return
1527 return
1531 if self._progbar is not None and self._progbar.printed:
1528 if self._progbar is not None and self._progbar.printed:
1532 self._progbar.clear()
1529 self._progbar.clear()
1533
1530
1534 def progress(self, topic, pos, item="", unit="", total=None):
1531 def progress(self, topic, pos, item="", unit="", total=None):
1535 '''show a progress message
1532 '''show a progress message
1536
1533
1537 By default a textual progress bar will be displayed if an operation
1534 By default a textual progress bar will be displayed if an operation
1538 takes too long. 'topic' is the current operation, 'item' is a
1535 takes too long. 'topic' is the current operation, 'item' is a
1539 non-numeric marker of the current position (i.e. the currently
1536 non-numeric marker of the current position (i.e. the currently
1540 in-process file), 'pos' is the current numeric position (i.e.
1537 in-process file), 'pos' is the current numeric position (i.e.
1541 revision, bytes, etc.), unit is a corresponding unit label,
1538 revision, bytes, etc.), unit is a corresponding unit label,
1542 and total is the highest expected pos.
1539 and total is the highest expected pos.
1543
1540
1544 Multiple nested topics may be active at a time.
1541 Multiple nested topics may be active at a time.
1545
1542
1546 All topics should be marked closed by setting pos to None at
1543 All topics should be marked closed by setting pos to None at
1547 termination.
1544 termination.
1548 '''
1545 '''
1549 if self._progbar is not None:
1546 if self._progbar is not None:
1550 self._progbar.progress(topic, pos, item=item, unit=unit,
1547 self._progbar.progress(topic, pos, item=item, unit=unit,
1551 total=total)
1548 total=total)
1552 if pos is None or not self.configbool('progress', 'debug'):
1549 if pos is None or not self.configbool('progress', 'debug'):
1553 return
1550 return
1554
1551
1555 if unit:
1552 if unit:
1556 unit = ' ' + unit
1553 unit = ' ' + unit
1557 if item:
1554 if item:
1558 item = ' ' + item
1555 item = ' ' + item
1559
1556
1560 if total:
1557 if total:
1561 pct = 100.0 * pos / total
1558 pct = 100.0 * pos / total
1562 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1559 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1563 % (topic, item, pos, total, unit, pct))
1560 % (topic, item, pos, total, unit, pct))
1564 else:
1561 else:
1565 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1562 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1566
1563
1567 def log(self, service, *msg, **opts):
1564 def log(self, service, *msg, **opts):
1568 '''hook for logging facility extensions
1565 '''hook for logging facility extensions
1569
1566
1570 service should be a readily-identifiable subsystem, which will
1567 service should be a readily-identifiable subsystem, which will
1571 allow filtering.
1568 allow filtering.
1572
1569
1573 *msg should be a newline-terminated format string to log, and
1570 *msg should be a newline-terminated format string to log, and
1574 then any values to %-format into that format string.
1571 then any values to %-format into that format string.
1575
1572
1576 **opts currently has no defined meanings.
1573 **opts currently has no defined meanings.
1577 '''
1574 '''
1578
1575
1579 def label(self, msg, label):
1576 def label(self, msg, label):
1580 '''style msg based on supplied label
1577 '''style msg based on supplied label
1581
1578
1582 If some color mode is enabled, this will add the necessary control
1579 If some color mode is enabled, this will add the necessary control
1583 characters to apply such color. In addition, 'debug' color mode adds
1580 characters to apply such color. In addition, 'debug' color mode adds
1584 markup showing which label affects a piece of text.
1581 markup showing which label affects a piece of text.
1585
1582
1586 ui.write(s, 'label') is equivalent to
1583 ui.write(s, 'label') is equivalent to
1587 ui.write(ui.label(s, 'label')).
1584 ui.write(ui.label(s, 'label')).
1588 '''
1585 '''
1589 if self._colormode is not None:
1586 if self._colormode is not None:
1590 return color.colorlabel(self, msg, label)
1587 return color.colorlabel(self, msg, label)
1591 return msg
1588 return msg
1592
1589
1593 def develwarn(self, msg, stacklevel=1, config=None):
1590 def develwarn(self, msg, stacklevel=1, config=None):
1594 """issue a developer warning message
1591 """issue a developer warning message
1595
1592
1596 Use 'stacklevel' to report the offender some layers further up in the
1593 Use 'stacklevel' to report the offender some layers further up in the
1597 stack.
1594 stack.
1598 """
1595 """
1599 if not self.configbool('devel', 'all-warnings'):
1596 if not self.configbool('devel', 'all-warnings'):
1600 if config is None or not self.configbool('devel', config):
1597 if config is None or not self.configbool('devel', config):
1601 return
1598 return
1602 msg = 'devel-warn: ' + msg
1599 msg = 'devel-warn: ' + msg
1603 stacklevel += 1 # get in develwarn
1600 stacklevel += 1 # get in develwarn
1604 if self.tracebackflag:
1601 if self.tracebackflag:
1605 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1602 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1606 self.log('develwarn', '%s at:\n%s' %
1603 self.log('develwarn', '%s at:\n%s' %
1607 (msg, ''.join(util.getstackframes(stacklevel))))
1604 (msg, ''.join(util.getstackframes(stacklevel))))
1608 else:
1605 else:
1609 curframe = inspect.currentframe()
1606 curframe = inspect.currentframe()
1610 calframe = inspect.getouterframes(curframe, 2)
1607 calframe = inspect.getouterframes(curframe, 2)
1611 self.write_err('%s at: %s:%s (%s)\n'
1608 self.write_err('%s at: %s:%s (%s)\n'
1612 % ((msg,) + calframe[stacklevel][1:4]))
1609 % ((msg,) + calframe[stacklevel][1:4]))
1613 self.log('develwarn', '%s at: %s:%s (%s)\n',
1610 self.log('develwarn', '%s at: %s:%s (%s)\n',
1614 msg, *calframe[stacklevel][1:4])
1611 msg, *calframe[stacklevel][1:4])
1615 curframe = calframe = None # avoid cycles
1612 curframe = calframe = None # avoid cycles
1616
1613
1617 def deprecwarn(self, msg, version, stacklevel=2):
1614 def deprecwarn(self, msg, version, stacklevel=2):
1618 """issue a deprecation warning
1615 """issue a deprecation warning
1619
1616
1620 - msg: message explaining what is deprecated and how to upgrade,
1617 - msg: message explaining what is deprecated and how to upgrade,
1621 - version: last version where the API will be supported,
1618 - version: last version where the API will be supported,
1622 """
1619 """
1623 if not (self.configbool('devel', 'all-warnings')
1620 if not (self.configbool('devel', 'all-warnings')
1624 or self.configbool('devel', 'deprec-warn')):
1621 or self.configbool('devel', 'deprec-warn')):
1625 return
1622 return
1626 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1623 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1627 " update your code.)") % version
1624 " update your code.)") % version
1628 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1625 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1629
1626
1630 def exportableenviron(self):
1627 def exportableenviron(self):
1631 """The environment variables that are safe to export, e.g. through
1628 """The environment variables that are safe to export, e.g. through
1632 hgweb.
1629 hgweb.
1633 """
1630 """
1634 return self._exportableenviron
1631 return self._exportableenviron
1635
1632
1636 @contextlib.contextmanager
1633 @contextlib.contextmanager
1637 def configoverride(self, overrides, source=""):
1634 def configoverride(self, overrides, source=""):
1638 """Context manager for temporary config overrides
1635 """Context manager for temporary config overrides
1639 `overrides` must be a dict of the following structure:
1636 `overrides` must be a dict of the following structure:
1640 {(section, name) : value}"""
1637 {(section, name) : value}"""
1641 backups = {}
1638 backups = {}
1642 try:
1639 try:
1643 for (section, name), value in overrides.items():
1640 for (section, name), value in overrides.items():
1644 backups[(section, name)] = self.backupconfig(section, name)
1641 backups[(section, name)] = self.backupconfig(section, name)
1645 self.setconfig(section, name, value, source)
1642 self.setconfig(section, name, value, source)
1646 yield
1643 yield
1647 finally:
1644 finally:
1648 for __, backup in backups.items():
1645 for __, backup in backups.items():
1649 self.restoreconfig(backup)
1646 self.restoreconfig(backup)
1650 # just restoring ui.quiet config to the previous value is not enough
1647 # just restoring ui.quiet config to the previous value is not enough
1651 # as it does not update ui.quiet class member
1648 # as it does not update ui.quiet class member
1652 if ('ui', 'quiet') in overrides:
1649 if ('ui', 'quiet') in overrides:
1653 self.fixconfig(section='ui')
1650 self.fixconfig(section='ui')
1654
1651
1655 class paths(dict):
1652 class paths(dict):
1656 """Represents a collection of paths and their configs.
1653 """Represents a collection of paths and their configs.
1657
1654
1658 Data is initially derived from ui instances and the config files they have
1655 Data is initially derived from ui instances and the config files they have
1659 loaded.
1656 loaded.
1660 """
1657 """
1661 def __init__(self, ui):
1658 def __init__(self, ui):
1662 dict.__init__(self)
1659 dict.__init__(self)
1663
1660
1664 for name, loc in ui.configitems('paths', ignoresub=True):
1661 for name, loc in ui.configitems('paths', ignoresub=True):
1665 # No location is the same as not existing.
1662 # No location is the same as not existing.
1666 if not loc:
1663 if not loc:
1667 continue
1664 continue
1668 loc, sub = ui.configsuboptions('paths', name)
1665 loc, sub = ui.configsuboptions('paths', name)
1669 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1666 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1670
1667
1671 def getpath(self, name, default=None):
1668 def getpath(self, name, default=None):
1672 """Return a ``path`` from a string, falling back to default.
1669 """Return a ``path`` from a string, falling back to default.
1673
1670
1674 ``name`` can be a named path or locations. Locations are filesystem
1671 ``name`` can be a named path or locations. Locations are filesystem
1675 paths or URIs.
1672 paths or URIs.
1676
1673
1677 Returns None if ``name`` is not a registered path, a URI, or a local
1674 Returns None if ``name`` is not a registered path, a URI, or a local
1678 path to a repo.
1675 path to a repo.
1679 """
1676 """
1680 # Only fall back to default if no path was requested.
1677 # Only fall back to default if no path was requested.
1681 if name is None:
1678 if name is None:
1682 if not default:
1679 if not default:
1683 default = ()
1680 default = ()
1684 elif not isinstance(default, (tuple, list)):
1681 elif not isinstance(default, (tuple, list)):
1685 default = (default,)
1682 default = (default,)
1686 for k in default:
1683 for k in default:
1687 try:
1684 try:
1688 return self[k]
1685 return self[k]
1689 except KeyError:
1686 except KeyError:
1690 continue
1687 continue
1691 return None
1688 return None
1692
1689
1693 # Most likely empty string.
1690 # Most likely empty string.
1694 # This may need to raise in the future.
1691 # This may need to raise in the future.
1695 if not name:
1692 if not name:
1696 return None
1693 return None
1697
1694
1698 try:
1695 try:
1699 return self[name]
1696 return self[name]
1700 except KeyError:
1697 except KeyError:
1701 # Try to resolve as a local path or URI.
1698 # Try to resolve as a local path or URI.
1702 try:
1699 try:
1703 # We don't pass sub-options in, so no need to pass ui instance.
1700 # We don't pass sub-options in, so no need to pass ui instance.
1704 return path(None, None, rawloc=name)
1701 return path(None, None, rawloc=name)
1705 except ValueError:
1702 except ValueError:
1706 raise error.RepoError(_('repository %s does not exist') %
1703 raise error.RepoError(_('repository %s does not exist') %
1707 name)
1704 name)
1708
1705
1709 _pathsuboptions = {}
1706 _pathsuboptions = {}
1710
1707
1711 def pathsuboption(option, attr):
1708 def pathsuboption(option, attr):
1712 """Decorator used to declare a path sub-option.
1709 """Decorator used to declare a path sub-option.
1713
1710
1714 Arguments are the sub-option name and the attribute it should set on
1711 Arguments are the sub-option name and the attribute it should set on
1715 ``path`` instances.
1712 ``path`` instances.
1716
1713
1717 The decorated function will receive as arguments a ``ui`` instance,
1714 The decorated function will receive as arguments a ``ui`` instance,
1718 ``path`` instance, and the string value of this option from the config.
1715 ``path`` instance, and the string value of this option from the config.
1719 The function should return the value that will be set on the ``path``
1716 The function should return the value that will be set on the ``path``
1720 instance.
1717 instance.
1721
1718
1722 This decorator can be used to perform additional verification of
1719 This decorator can be used to perform additional verification of
1723 sub-options and to change the type of sub-options.
1720 sub-options and to change the type of sub-options.
1724 """
1721 """
1725 def register(func):
1722 def register(func):
1726 _pathsuboptions[option] = (attr, func)
1723 _pathsuboptions[option] = (attr, func)
1727 return func
1724 return func
1728 return register
1725 return register
1729
1726
1730 @pathsuboption('pushurl', 'pushloc')
1727 @pathsuboption('pushurl', 'pushloc')
1731 def pushurlpathoption(ui, path, value):
1728 def pushurlpathoption(ui, path, value):
1732 u = util.url(value)
1729 u = util.url(value)
1733 # Actually require a URL.
1730 # Actually require a URL.
1734 if not u.scheme:
1731 if not u.scheme:
1735 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1732 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1736 return None
1733 return None
1737
1734
1738 # Don't support the #foo syntax in the push URL to declare branch to
1735 # Don't support the #foo syntax in the push URL to declare branch to
1739 # push.
1736 # push.
1740 if u.fragment:
1737 if u.fragment:
1741 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1738 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1742 'ignoring)\n') % path.name)
1739 'ignoring)\n') % path.name)
1743 u.fragment = None
1740 u.fragment = None
1744
1741
1745 return str(u)
1742 return str(u)
1746
1743
1747 @pathsuboption('pushrev', 'pushrev')
1744 @pathsuboption('pushrev', 'pushrev')
1748 def pushrevpathoption(ui, path, value):
1745 def pushrevpathoption(ui, path, value):
1749 return value
1746 return value
1750
1747
1751 class path(object):
1748 class path(object):
1752 """Represents an individual path and its configuration."""
1749 """Represents an individual path and its configuration."""
1753
1750
1754 def __init__(self, ui, name, rawloc=None, suboptions=None):
1751 def __init__(self, ui, name, rawloc=None, suboptions=None):
1755 """Construct a path from its config options.
1752 """Construct a path from its config options.
1756
1753
1757 ``ui`` is the ``ui`` instance the path is coming from.
1754 ``ui`` is the ``ui`` instance the path is coming from.
1758 ``name`` is the symbolic name of the path.
1755 ``name`` is the symbolic name of the path.
1759 ``rawloc`` is the raw location, as defined in the config.
1756 ``rawloc`` is the raw location, as defined in the config.
1760 ``pushloc`` is the raw locations pushes should be made to.
1757 ``pushloc`` is the raw locations pushes should be made to.
1761
1758
1762 If ``name`` is not defined, we require that the location be a) a local
1759 If ``name`` is not defined, we require that the location be a) a local
1763 filesystem path with a .hg directory or b) a URL. If not,
1760 filesystem path with a .hg directory or b) a URL. If not,
1764 ``ValueError`` is raised.
1761 ``ValueError`` is raised.
1765 """
1762 """
1766 if not rawloc:
1763 if not rawloc:
1767 raise ValueError('rawloc must be defined')
1764 raise ValueError('rawloc must be defined')
1768
1765
1769 # Locations may define branches via syntax <base>#<branch>.
1766 # Locations may define branches via syntax <base>#<branch>.
1770 u = util.url(rawloc)
1767 u = util.url(rawloc)
1771 branch = None
1768 branch = None
1772 if u.fragment:
1769 if u.fragment:
1773 branch = u.fragment
1770 branch = u.fragment
1774 u.fragment = None
1771 u.fragment = None
1775
1772
1776 self.url = u
1773 self.url = u
1777 self.branch = branch
1774 self.branch = branch
1778
1775
1779 self.name = name
1776 self.name = name
1780 self.rawloc = rawloc
1777 self.rawloc = rawloc
1781 self.loc = '%s' % u
1778 self.loc = '%s' % u
1782
1779
1783 # When given a raw location but not a symbolic name, validate the
1780 # When given a raw location but not a symbolic name, validate the
1784 # location is valid.
1781 # location is valid.
1785 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1782 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1786 raise ValueError('location is not a URL or path to a local '
1783 raise ValueError('location is not a URL or path to a local '
1787 'repo: %s' % rawloc)
1784 'repo: %s' % rawloc)
1788
1785
1789 suboptions = suboptions or {}
1786 suboptions = suboptions or {}
1790
1787
1791 # Now process the sub-options. If a sub-option is registered, its
1788 # Now process the sub-options. If a sub-option is registered, its
1792 # attribute will always be present. The value will be None if there
1789 # attribute will always be present. The value will be None if there
1793 # was no valid sub-option.
1790 # was no valid sub-option.
1794 for suboption, (attr, func) in _pathsuboptions.iteritems():
1791 for suboption, (attr, func) in _pathsuboptions.iteritems():
1795 if suboption not in suboptions:
1792 if suboption not in suboptions:
1796 setattr(self, attr, None)
1793 setattr(self, attr, None)
1797 continue
1794 continue
1798
1795
1799 value = func(ui, self, suboptions[suboption])
1796 value = func(ui, self, suboptions[suboption])
1800 setattr(self, attr, value)
1797 setattr(self, attr, value)
1801
1798
1802 def _isvalidlocalpath(self, path):
1799 def _isvalidlocalpath(self, path):
1803 """Returns True if the given path is a potentially valid repository.
1800 """Returns True if the given path is a potentially valid repository.
1804 This is its own function so that extensions can change the definition of
1801 This is its own function so that extensions can change the definition of
1805 'valid' in this case (like when pulling from a git repo into a hg
1802 'valid' in this case (like when pulling from a git repo into a hg
1806 one)."""
1803 one)."""
1807 return os.path.isdir(os.path.join(path, '.hg'))
1804 return os.path.isdir(os.path.join(path, '.hg'))
1808
1805
1809 @property
1806 @property
1810 def suboptions(self):
1807 def suboptions(self):
1811 """Return sub-options and their values for this path.
1808 """Return sub-options and their values for this path.
1812
1809
1813 This is intended to be used for presentation purposes.
1810 This is intended to be used for presentation purposes.
1814 """
1811 """
1815 d = {}
1812 d = {}
1816 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1813 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1817 value = getattr(self, attr)
1814 value = getattr(self, attr)
1818 if value is not None:
1815 if value is not None:
1819 d[subopt] = value
1816 d[subopt] = value
1820 return d
1817 return d
1821
1818
1822 # we instantiate one globally shared progress bar to avoid
1819 # we instantiate one globally shared progress bar to avoid
1823 # competing progress bars when multiple UI objects get created
1820 # competing progress bars when multiple UI objects get created
1824 _progresssingleton = None
1821 _progresssingleton = None
1825
1822
1826 def getprogbar(ui):
1823 def getprogbar(ui):
1827 global _progresssingleton
1824 global _progresssingleton
1828 if _progresssingleton is None:
1825 if _progresssingleton is None:
1829 # passing 'ui' object to the singleton is fishy,
1826 # passing 'ui' object to the singleton is fishy,
1830 # this is how the extension used to work but feel free to rework it.
1827 # this is how the extension used to work but feel free to rework it.
1831 _progresssingleton = progress.progbar(ui)
1828 _progresssingleton = progress.progbar(ui)
1832 return _progresssingleton
1829 return _progresssingleton
1833
1830
1834 def haveprogbar():
1831 def haveprogbar():
1835 return _progresssingleton is not None
1832 return _progresssingleton is not None
General Comments 0
You need to be logged in to leave comments. Login now