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