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