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