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