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