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