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