##// END OF EJS Templates
python2-compat: fix extensions' tests on python2...
marmoute -
r49239:0b2469bf default
parent child Browse files
Show More
@@ -1,167 +1,171 b''
1 1 #require no-chg
2 2 $ filterlog () {
3 3 > sed -e 's!^[0-9/]* [0-9:]* ([0-9]*)>!YYYY/MM/DD HH:MM:SS (PID)>!'
4 4 > }
5 5
6 6 ensure that failing ui.atexit handlers report sensibly
7 7
8 8 $ cat > $TESTTMP/bailatexit.py <<EOF
9 9 > from mercurial import util
10 10 > def bail():
11 11 > raise RuntimeError('ui.atexit handler exception')
12 12 >
13 13 > def extsetup(ui):
14 14 > ui.atexit(bail)
15 15 > EOF
16 16 $ hg -q --config extensions.bailatexit=$TESTTMP/bailatexit.py \
17 17 > help help
18 18 hg help [-eck] [-s PLATFORM] [TOPIC]
19 19
20 20 show help for a given topic or a help overview
21 21 error in exit handlers:
22 22 Traceback (most recent call last):
23 23 File "*/mercurial/dispatch.py", line *, in _runexithandlers (glob) (no-pyoxidizer !)
24 24 File "mercurial.dispatch", line *, in _runexithandlers (glob) (pyoxidizer !)
25 25 func(*args, **kwargs)
26 26 File "$TESTTMP/bailatexit.py", line *, in bail (glob)
27 27 raise RuntimeError('ui.atexit handler exception')
28 28 RuntimeError: ui.atexit handler exception
29 29 [255]
30 30
31 31 $ rm $TESTTMP/bailatexit.py
32 32
33 33 another bad extension
34 34
35 35 $ echo 'raise Exception("bit bucket overflow")' > badext.py
36 36 $ abspathexc=`pwd`/badext.py
37 37
38 38 $ cat >baddocext.py <<EOF
39 39 > """
40 40 > baddocext is bad
41 41 > """
42 42 > EOF
43 43 $ abspathdoc=`pwd`/baddocext.py
44 44
45 45 $ cat <<EOF >> $HGRCPATH
46 46 > [extensions]
47 47 > gpg =
48 48 > hgext.gpg =
49 49 > badext = $abspathexc
50 50 > baddocext = $abspathdoc
51 51 > badext2 =
52 52 > EOF
53 53
54 54 $ hg -q help help 2>&1 |grep extension
55 55 *** failed to import extension "badext" from $TESTTMP/badext.py: bit bucket overflow
56 *** failed to import extension "badext2": No module named 'badext2'
56 *** failed to import extension "badext2": No module named 'badext2' (py3 !)
57 *** failed to import extension "badext2": No module named badext2 (no-py3 !)
57 58
58 59 show traceback
59 60
60 61 $ hg -q help help --traceback 2>&1 | egrep ' extension|^Exception|Traceback|ImportError|ModuleNotFound'
61 62 *** failed to import extension "badext" from $TESTTMP/badext.py: bit bucket overflow
62 63 Traceback (most recent call last):
63 64 Exception: bit bucket overflow
64 *** failed to import extension "badext2": No module named 'badext2'
65 *** failed to import extension "badext2": No module named 'badext2' (py3 !)
66 *** failed to import extension "badext2": No module named badext2 (no-py3 !)
65 67 Traceback (most recent call last):
66 68 ImportError: No module named badext2 (no-py3 !)
67 69 ImportError: No module named 'hgext.badext2' (py3 no-py36 !)
68 70 ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
69 71 Traceback (most recent call last): (py3 !)
70 72 ImportError: No module named 'hgext3rd.badext2' (py3 no-py36 !)
71 73 ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
72 74 Traceback (most recent call last): (py3 !)
73 75 ImportError: No module named 'badext2' (py3 no-py36 !)
74 76 ModuleNotFoundError: No module named 'badext2' (py36 !)
75 77
76 78 names of extensions failed to load can be accessed via extensions.notloaded()
77 79
78 80 $ cat <<EOF > showbadexts.py
79 81 > from mercurial import commands, extensions, registrar
80 82 > cmdtable = {}
81 83 > command = registrar.command(cmdtable)
82 84 > @command(b'showbadexts', norepo=True)
83 85 > def showbadexts(ui, *pats, **opts):
84 86 > ui.write(b'BADEXTS: %s\n' % b' '.join(sorted(extensions.notloaded())))
85 87 > EOF
86 88 $ hg --config extensions.badexts=showbadexts.py showbadexts 2>&1 | grep '^BADEXTS'
87 89 BADEXTS: badext badext2
88 90
89 91 #if no-extraextensions
90 92 show traceback for ImportError of hgext.name if devel.debug.extensions is set
91 93
92 94 $ (hg help help --traceback --debug --config devel.debug.extensions=yes 2>&1) \
93 95 > | grep -v '^ ' \
94 96 > | filterlog \
95 97 > | egrep 'extension..[^p]|^Exception|Traceback|ImportError|^YYYY|not import|ModuleNotFound'
96 98 YYYY/MM/DD HH:MM:SS (PID)> loading extensions
97 99 YYYY/MM/DD HH:MM:SS (PID)> - processing 5 entries
98 100 YYYY/MM/DD HH:MM:SS (PID)> - loading extension: gpg
99 101 YYYY/MM/DD HH:MM:SS (PID)> > gpg extension loaded in * (glob)
100 102 YYYY/MM/DD HH:MM:SS (PID)> - validating extension tables: gpg
101 103 YYYY/MM/DD HH:MM:SS (PID)> - invoking registered callbacks: gpg
102 104 YYYY/MM/DD HH:MM:SS (PID)> > callbacks completed in * (glob)
103 105 YYYY/MM/DD HH:MM:SS (PID)> - loading extension: badext
104 106 *** failed to import extension "badext" from $TESTTMP/badext.py: bit bucket overflow
105 107 Traceback (most recent call last):
106 108 Exception: bit bucket overflow
107 109 YYYY/MM/DD HH:MM:SS (PID)> - loading extension: baddocext
108 110 YYYY/MM/DD HH:MM:SS (PID)> > baddocext extension loaded in * (glob)
109 111 YYYY/MM/DD HH:MM:SS (PID)> - validating extension tables: baddocext
110 112 YYYY/MM/DD HH:MM:SS (PID)> - invoking registered callbacks: baddocext
111 113 YYYY/MM/DD HH:MM:SS (PID)> > callbacks completed in * (glob)
112 114 YYYY/MM/DD HH:MM:SS (PID)> - loading extension: badext2
113 115 YYYY/MM/DD HH:MM:SS (PID)> - could not import hgext.badext2 (No module named *badext2*): trying hgext3rd.badext2 (glob)
114 116 Traceback (most recent call last):
115 117 ImportError: No module named badext2 (no-py3 !)
116 118 ImportError: No module named 'hgext.badext2' (py3 no-py36 !)
117 119 ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
118 120 YYYY/MM/DD HH:MM:SS (PID)> - could not import hgext3rd.badext2 (No module named *badext2*): trying badext2 (glob)
119 121 Traceback (most recent call last):
120 122 ImportError: No module named badext2 (no-py3 !)
121 123 ImportError: No module named 'hgext.badext2' (py3 no-py36 !)
122 124 ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
123 125 Traceback (most recent call last): (py3 !)
124 126 ImportError: No module named 'hgext3rd.badext2' (py3 no-py36 !)
125 127 ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
126 *** failed to import extension "badext2": No module named 'badext2'
128 *** failed to import extension "badext2": No module named 'badext2' (py3 !)
129 *** failed to import extension "badext2": No module named badext2 (no-py3 !)
127 130 Traceback (most recent call last):
128 131 ImportError: No module named 'hgext.badext2' (py3 no-py36 !)
129 132 ModuleNotFoundError: No module named 'hgext.badext2' (py36 !)
130 133 Traceback (most recent call last): (py3 !)
131 134 ImportError: No module named 'hgext3rd.badext2' (py3 no-py36 !)
132 135 ModuleNotFoundError: No module named 'hgext3rd.badext2' (py36 !)
133 136 Traceback (most recent call last): (py3 !)
134 137 ModuleNotFoundError: No module named 'badext2' (py36 !)
135 138 ImportError: No module named 'badext2' (py3 no-py36 !)
136 139 ImportError: No module named badext2 (no-py3 !)
137 140 YYYY/MM/DD HH:MM:SS (PID)> > loaded 2 extensions, total time * (glob)
138 141 YYYY/MM/DD HH:MM:SS (PID)> - loading configtable attributes
139 142 YYYY/MM/DD HH:MM:SS (PID)> - executing uisetup hooks
140 143 YYYY/MM/DD HH:MM:SS (PID)> - running uisetup for gpg
141 144 YYYY/MM/DD HH:MM:SS (PID)> > uisetup for gpg took * (glob)
142 145 YYYY/MM/DD HH:MM:SS (PID)> - running uisetup for baddocext
143 146 YYYY/MM/DD HH:MM:SS (PID)> > uisetup for baddocext took * (glob)
144 147 YYYY/MM/DD HH:MM:SS (PID)> > all uisetup took * (glob)
145 148 YYYY/MM/DD HH:MM:SS (PID)> - executing extsetup hooks
146 149 YYYY/MM/DD HH:MM:SS (PID)> - running extsetup for gpg
147 150 YYYY/MM/DD HH:MM:SS (PID)> > extsetup for gpg took * (glob)
148 151 YYYY/MM/DD HH:MM:SS (PID)> - running extsetup for baddocext
149 152 YYYY/MM/DD HH:MM:SS (PID)> > extsetup for baddocext took * (glob)
150 153 YYYY/MM/DD HH:MM:SS (PID)> > all extsetup took * (glob)
151 154 YYYY/MM/DD HH:MM:SS (PID)> - executing remaining aftercallbacks
152 155 YYYY/MM/DD HH:MM:SS (PID)> > remaining aftercallbacks completed in * (glob)
153 156 YYYY/MM/DD HH:MM:SS (PID)> - loading extension registration objects
154 157 YYYY/MM/DD HH:MM:SS (PID)> > extension registration object loading took * (glob)
155 158 YYYY/MM/DD HH:MM:SS (PID)> > extension baddocext take a total of * to load (glob)
156 159 YYYY/MM/DD HH:MM:SS (PID)> > extension gpg take a total of * to load (glob)
157 160 YYYY/MM/DD HH:MM:SS (PID)> extension loading complete
158 161 #endif
159 162
160 163 confirm that there's no crash when an extension's documentation is bad
161 164
162 165 $ hg help --keyword baddocext
163 166 *** failed to import extension "badext" from $TESTTMP/badext.py: bit bucket overflow
164 *** failed to import extension "badext2": No module named 'badext2'
167 *** failed to import extension "badext2": No module named 'badext2' (py3 !)
168 *** failed to import extension "badext2": No module named badext2 (no-py3 !)
165 169 Topics:
166 170
167 171 extensions Using Additional Features
@@ -1,2054 +1,2055 b''
1 1 Test basic extension support
2 2 $ cat > unflush.py <<EOF
3 3 > import sys
4 4 > from mercurial import pycompat
5 5 > if pycompat.ispy3:
6 6 > # no changes required
7 7 > sys.exit(0)
8 8 > with open(sys.argv[1], 'rb') as f:
9 9 > data = f.read()
10 10 > with open(sys.argv[1], 'wb') as f:
11 11 > f.write(data.replace(b', flush=True', b''))
12 12 > EOF
13 13
14 14 $ cat > foobar.py <<EOF
15 15 > import os
16 16 > from mercurial import commands, exthelper, registrar
17 17 >
18 18 > eh = exthelper.exthelper()
19 19 > eh.configitem(b'tests', b'foo', default=b"Foo")
20 20 >
21 21 > uisetup = eh.finaluisetup
22 22 > uipopulate = eh.finaluipopulate
23 23 > reposetup = eh.finalreposetup
24 24 > cmdtable = eh.cmdtable
25 25 > configtable = eh.configtable
26 26 >
27 27 > @eh.uisetup
28 28 > def _uisetup(ui):
29 29 > ui.debug(b"uisetup called [debug]\\n")
30 30 > ui.write(b"uisetup called\\n")
31 31 > ui.status(b"uisetup called [status]\\n")
32 32 > ui.flush()
33 33 > @eh.uipopulate
34 34 > def _uipopulate(ui):
35 35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
36 36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
37 37 > @eh.reposetup
38 38 > def _reposetup(ui, repo):
39 39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
40 40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
41 41 > ui.flush()
42 42 > @eh.command(b'foo', [], b'hg foo')
43 43 > def foo(ui, *args, **kwargs):
44 44 > foo = ui.config(b'tests', b'foo')
45 45 > ui.write(foo)
46 46 > ui.write(b"\\n")
47 47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
48 48 > def bar(ui, *args, **kwargs):
49 49 > ui.write(b"Bar\\n")
50 50 > EOF
51 51 $ abspath=`pwd`/foobar.py
52 52
53 53 $ mkdir barfoo
54 54 $ cp foobar.py barfoo/__init__.py
55 55 $ barfoopath=`pwd`/barfoo
56 56
57 57 $ hg init a
58 58 $ cd a
59 59 $ echo foo > file
60 60 $ hg add file
61 61 $ hg commit -m 'add file'
62 62
63 63 $ echo '[extensions]' >> $HGRCPATH
64 64 $ echo "foobar = $abspath" >> $HGRCPATH
65 65 $ hg foo
66 66 uisetup called
67 67 uisetup called [status]
68 68 uipopulate called (1 times)
69 69 uipopulate called (1 times)
70 70 uipopulate called (1 times)
71 71 reposetup called for a
72 72 ui == repo.ui
73 73 uipopulate called (1 times) (chg !)
74 74 uipopulate called (1 times) (chg !)
75 75 uipopulate called (1 times) (chg !)
76 76 uipopulate called (1 times) (chg !)
77 77 uipopulate called (1 times) (chg !)
78 78 reposetup called for a (chg !)
79 79 ui == repo.ui (chg !)
80 80 Foo
81 81 $ hg foo --quiet
82 82 uisetup called (no-chg !)
83 83 uipopulate called (1 times)
84 84 uipopulate called (1 times)
85 85 uipopulate called (1 times) (chg !)
86 86 uipopulate called (1 times) (chg !)
87 87 uipopulate called (1 times)
88 88 reposetup called for a
89 89 ui == repo.ui
90 90 Foo
91 91 $ hg foo --debug
92 92 uisetup called [debug] (no-chg !)
93 93 uisetup called (no-chg !)
94 94 uisetup called [status] (no-chg !)
95 95 uipopulate called (1 times)
96 96 uipopulate called (1 times)
97 97 uipopulate called (1 times) (chg !)
98 98 uipopulate called (1 times) (chg !)
99 99 uipopulate called (1 times)
100 100 reposetup called for a
101 101 ui == repo.ui
102 102 Foo
103 103
104 104 $ cd ..
105 105 $ hg clone a b
106 106 uisetup called (no-chg !)
107 107 uisetup called [status] (no-chg !)
108 108 uipopulate called (1 times)
109 109 uipopulate called (1 times) (chg !)
110 110 uipopulate called (1 times)
111 111 reposetup called for a
112 112 ui == repo.ui
113 113 uipopulate called (1 times)
114 114 uipopulate called (1 times)
115 115 reposetup called for b
116 116 ui == repo.ui
117 117 updating to branch default
118 118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 119
120 120 $ hg bar
121 121 uisetup called (no-chg !)
122 122 uisetup called [status] (no-chg !)
123 123 uipopulate called (1 times)
124 124 uipopulate called (1 times) (chg !)
125 125 Bar
126 126 $ echo 'foobar = !' >> $HGRCPATH
127 127
128 128 module/__init__.py-style
129 129
130 130 $ echo "barfoo = $barfoopath" >> $HGRCPATH
131 131 $ cd a
132 132 $ hg foo
133 133 uisetup called
134 134 uisetup called [status]
135 135 uipopulate called (1 times)
136 136 uipopulate called (1 times)
137 137 uipopulate called (1 times)
138 138 reposetup called for a
139 139 ui == repo.ui
140 140 uipopulate called (1 times) (chg !)
141 141 uipopulate called (1 times) (chg !)
142 142 uipopulate called (1 times) (chg !)
143 143 uipopulate called (1 times) (chg !)
144 144 uipopulate called (1 times) (chg !)
145 145 reposetup called for a (chg !)
146 146 ui == repo.ui (chg !)
147 147 Foo
148 148 $ echo 'barfoo = !' >> $HGRCPATH
149 149
150 150 Check that extensions are loaded in phases:
151 151
152 152 $ cat > foo.py <<EOF
153 153 > from __future__ import print_function
154 154 > import os
155 155 > from mercurial import exthelper
156 156 > from mercurial.utils import procutil
157 157 >
158 158 > def write(msg):
159 159 > procutil.stdout.write(msg)
160 160 > procutil.stdout.flush()
161 161 >
162 162 > name = os.path.basename(__file__).rsplit('.', 1)[0]
163 163 > bytesname = name.encode('utf-8')
164 164 > write(b"1) %s imported\n" % bytesname)
165 165 > eh = exthelper.exthelper()
166 166 > @eh.uisetup
167 167 > def _uisetup(ui):
168 168 > write(b"2) %s uisetup\n" % bytesname)
169 169 > @eh.extsetup
170 170 > def _extsetup(ui):
171 171 > write(b"3) %s extsetup\n" % bytesname)
172 172 > @eh.uipopulate
173 173 > def _uipopulate(ui):
174 174 > write(b"4) %s uipopulate\n" % bytesname)
175 175 > @eh.reposetup
176 176 > def _reposetup(ui, repo):
177 177 > write(b"5) %s reposetup\n" % bytesname)
178 178 >
179 179 > extsetup = eh.finalextsetup
180 180 > reposetup = eh.finalreposetup
181 181 > uipopulate = eh.finaluipopulate
182 182 > uisetup = eh.finaluisetup
183 183 > revsetpredicate = eh.revsetpredicate
184 184 >
185 185 > # custom predicate to check registration of functions at loading
186 186 > from mercurial import (
187 187 > smartset,
188 188 > )
189 189 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
190 190 > def custompredicate(repo, subset, x):
191 191 > return smartset.baseset([r for r in subset if r in {0}])
192 192 > EOF
193 193 $ "$PYTHON" $TESTTMP/unflush.py foo.py
194 194
195 195 $ cp foo.py bar.py
196 196 $ echo 'foo = foo.py' >> $HGRCPATH
197 197 $ echo 'bar = bar.py' >> $HGRCPATH
198 198
199 199 Check normal command's load order of extensions and registration of functions
200 200
201 201 On chg server, extension should be first set up by the server. Then
202 202 object-level setup should follow in the worker process.
203 203
204 204 $ hg log -r "foo() and bar()" -q
205 205 1) foo imported
206 206 1) bar imported
207 207 2) foo uisetup
208 208 2) bar uisetup
209 209 3) foo extsetup
210 210 3) bar extsetup
211 211 4) foo uipopulate
212 212 4) bar uipopulate
213 213 4) foo uipopulate
214 214 4) bar uipopulate
215 215 4) foo uipopulate
216 216 4) bar uipopulate
217 217 5) foo reposetup
218 218 5) bar reposetup
219 219 4) foo uipopulate (chg !)
220 220 4) bar uipopulate (chg !)
221 221 4) foo uipopulate (chg !)
222 222 4) bar uipopulate (chg !)
223 223 4) foo uipopulate (chg !)
224 224 4) bar uipopulate (chg !)
225 225 4) foo uipopulate (chg !)
226 226 4) bar uipopulate (chg !)
227 227 4) foo uipopulate (chg !)
228 228 4) bar uipopulate (chg !)
229 229 5) foo reposetup (chg !)
230 230 5) bar reposetup (chg !)
231 231 0:c24b9ac61126
232 232
233 233 Check hgweb's load order of extensions and registration of functions
234 234
235 235 $ cat > hgweb.cgi <<EOF
236 236 > #!$PYTHON
237 237 > from mercurial import demandimport; demandimport.enable()
238 238 > from mercurial.hgweb import hgweb
239 239 > from mercurial.hgweb import wsgicgi
240 240 > application = hgweb(b'.', b'test repo')
241 241 > wsgicgi.launch(application)
242 242 > EOF
243 243 $ . "$TESTDIR/cgienv"
244 244
245 245 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
246 246 > | grep '^[0-9]) ' # ignores HTML output
247 247 1) foo imported
248 248 1) bar imported
249 249 2) foo uisetup
250 250 2) bar uisetup
251 251 3) foo extsetup
252 252 3) bar extsetup
253 253 4) foo uipopulate
254 254 4) bar uipopulate
255 255 4) foo uipopulate
256 256 4) bar uipopulate
257 257 5) foo reposetup
258 258 5) bar reposetup
259 259
260 260 (check that revset predicate foo() and bar() are available)
261 261
262 262 #if msys
263 263 $ PATH_INFO='//shortlog'
264 264 #else
265 265 $ PATH_INFO='/shortlog'
266 266 #endif
267 267 $ export PATH_INFO
268 268 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
269 269 > | grep '<a href="/rev/[0-9a-z]*">'
270 270 <a href="/rev/c24b9ac61126">add file</a>
271 271
272 272 $ echo 'foo = !' >> $HGRCPATH
273 273 $ echo 'bar = !' >> $HGRCPATH
274 274
275 275 Check "from __future__ import absolute_import" support for external libraries
276 276
277 277 (import-checker.py reports issues for some of heredoc python code
278 278 fragments below, because import-checker.py does not know test specific
279 279 package hierarchy. NO_CHECK_* should be used as a limit mark of
280 280 heredoc, in order to make import-checker.py ignore them. For
281 281 simplicity, all python code fragments below are generated with such
282 282 limit mark, regardless of importing module or not.)
283 283
284 284 #if windows
285 285 $ PATHSEP=";"
286 286 #else
287 287 $ PATHSEP=":"
288 288 #endif
289 289 $ export PATHSEP
290 290
291 291 $ mkdir $TESTTMP/libroot
292 292 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
293 293 $ mkdir $TESTTMP/libroot/mod
294 294 $ touch $TESTTMP/libroot/mod/__init__.py
295 295 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
296 296
297 297 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
298 298 > from __future__ import absolute_import, print_function
299 299 > import ambig # should load "libroot/ambig.py"
300 300 > s = ambig.s
301 301 > NO_CHECK_EOF
302 302 $ cat > loadabs.py <<NO_CHECK_EOF
303 303 > import mod.ambigabs as ambigabs
304 304 > def extsetup(ui):
305 305 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
306 306 > NO_CHECK_EOF
307 307 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
308 308 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
309 309 ambigabs.s=libroot/ambig.py
310 310 $TESTTMP/a
311 311
312 312 #if no-py3
313 313 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
314 314 > from __future__ import print_function
315 315 > import ambig # should load "libroot/mod/ambig.py"
316 316 > s = ambig.s
317 317 > NO_CHECK_EOF
318 318 $ cat > loadrel.py <<NO_CHECK_EOF
319 319 > import mod.ambigrel as ambigrel
320 320 > def extsetup(ui):
321 321 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
322 322 > NO_CHECK_EOF
323 323 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
324 324 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
325 325 ambigrel.s=libroot/mod/ambig.py
326 326 $TESTTMP/a
327 327 #endif
328 328
329 329 Check absolute/relative import of extension specific modules
330 330
331 331 $ mkdir $TESTTMP/extroot
332 332 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
333 333 > s = b'this is extroot.bar'
334 334 > NO_CHECK_EOF
335 335 $ mkdir $TESTTMP/extroot/sub1
336 336 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
337 337 > s = b'this is extroot.sub1.__init__'
338 338 > NO_CHECK_EOF
339 339 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
340 340 > s = b'this is extroot.sub1.baz'
341 341 > NO_CHECK_EOF
342 342 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
343 343 > from __future__ import absolute_import
344 344 > s = b'this is extroot.__init__'
345 345 > from . import foo
346 346 > def extsetup(ui):
347 347 > ui.write(b'(extroot) ', foo.func(), b'\n')
348 348 > ui.flush()
349 349 > NO_CHECK_EOF
350 350
351 351 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
352 352 > # test absolute import
353 353 > buf = []
354 354 > def func():
355 355 > # "not locals" case
356 356 > import extroot.bar
357 357 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
358 358 > return b'\n(extroot) '.join(buf)
359 359 > # b"fromlist == ('*',)" case
360 360 > from extroot.bar import *
361 361 > buf.append(b'from extroot.bar import *: %s' % s)
362 362 > # "not fromlist" and "if '.' in name" case
363 363 > import extroot.sub1.baz
364 364 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
365 365 > # "not fromlist" and NOT "if '.' in name" case
366 366 > import extroot
367 367 > buf.append(b'import extroot: %s' % extroot.s)
368 368 > # NOT "not fromlist" and NOT "level != -1" case
369 369 > from extroot.bar import s
370 370 > buf.append(b'from extroot.bar import s: %s' % s)
371 371 > NO_CHECK_EOF
372 372 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
373 373 (extroot) from extroot.bar import *: this is extroot.bar
374 374 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
375 375 (extroot) import extroot: this is extroot.__init__
376 376 (extroot) from extroot.bar import s: this is extroot.bar
377 377 (extroot) import extroot.bar in func(): this is extroot.bar
378 378 $TESTTMP/a
379 379
380 380 #if no-py3
381 381 $ rm "$TESTTMP"/extroot/foo.*
382 382 $ rm -Rf "$TESTTMP/extroot/__pycache__"
383 383 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
384 384 > # test relative import
385 385 > buf = []
386 386 > def func():
387 387 > # "not locals" case
388 388 > import bar
389 389 > buf.append('import bar in func(): %s' % bar.s)
390 390 > return '\n(extroot) '.join(buf)
391 391 > # "fromlist == ('*',)" case
392 392 > from bar import *
393 393 > buf.append('from bar import *: %s' % s)
394 394 > # "not fromlist" and "if '.' in name" case
395 395 > import sub1.baz
396 396 > buf.append('import sub1.baz: %s' % sub1.baz.s)
397 397 > # "not fromlist" and NOT "if '.' in name" case
398 398 > import sub1
399 399 > buf.append('import sub1: %s' % sub1.s)
400 400 > # NOT "not fromlist" and NOT "level != -1" case
401 401 > from bar import s
402 402 > buf.append('from bar import s: %s' % s)
403 403 > NO_CHECK_EOF
404 404 $ hg --config extensions.extroot=$TESTTMP/extroot root
405 405 (extroot) from bar import *: this is extroot.bar
406 406 (extroot) import sub1.baz: this is extroot.sub1.baz
407 407 (extroot) import sub1: this is extroot.sub1.__init__
408 408 (extroot) from bar import s: this is extroot.bar
409 409 (extroot) import bar in func(): this is extroot.bar
410 410 $TESTTMP/a
411 411 #endif
412 412
413 413 #if demandimport
414 414
415 415 Examine whether module loading is delayed until actual referring, even
416 416 though module is imported with "absolute_import" feature.
417 417
418 418 Files below in each packages are used for described purpose:
419 419
420 420 - "called": examine whether "from MODULE import ATTR" works correctly
421 421 - "unused": examine whether loading is delayed correctly
422 422 - "used": examine whether "from PACKAGE import MODULE" works correctly
423 423
424 424 Package hierarchy is needed to examine whether demand importing works
425 425 as expected for "from SUB.PACK.AGE import MODULE".
426 426
427 427 Setup "external library" to be imported with "absolute_import"
428 428 feature.
429 429
430 430 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
431 431 $ touch $TESTTMP/extlibroot/__init__.py
432 432 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
433 433 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
434 434
435 435 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
436 436 > def func():
437 437 > return b"this is extlibroot.lsub1.lsub2.called.func()"
438 438 > NO_CHECK_EOF
439 439 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
440 440 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
441 441 > NO_CHECK_EOF
442 442 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
443 443 > detail = b"this is extlibroot.lsub1.lsub2.used"
444 444 > NO_CHECK_EOF
445 445
446 446 Setup sub-package of "external library", which causes instantiation of
447 447 demandmod in "recurse down the module chain" code path. Relative
448 448 importing with "absolute_import" feature isn't tested, because "level
449 449 >=1 " doesn't cause instantiation of demandmod.
450 450
451 451 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
452 452 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
453 453 > detail = b"this is extlibroot.recursedown.abs.used"
454 454 > NO_CHECK_EOF
455 455 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
456 456 > from __future__ import absolute_import
457 457 > from extlibroot.recursedown.abs.used import detail
458 458 > NO_CHECK_EOF
459 459
460 460 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
461 461 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
462 462 > detail = b"this is extlibroot.recursedown.legacy.used"
463 463 > NO_CHECK_EOF
464 464 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
465 465 > # legacy style (level == -1) import
466 466 > from extlibroot.recursedown.legacy.used import detail
467 467 > NO_CHECK_EOF
468 468
469 469 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
470 470 > from __future__ import absolute_import
471 471 > from extlibroot.recursedown.abs import detail as absdetail
472 472 > from .legacy import detail as legacydetail
473 473 > NO_CHECK_EOF
474 474
475 475 Setup package that re-exports an attribute of its submodule as the same
476 476 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
477 477 the submodule 'used' should be somehow accessible. (issue5617)
478 478
479 479 $ mkdir -p $TESTTMP/extlibroot/shadowing
480 480 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
481 481 > detail = b"this is extlibroot.shadowing.used"
482 482 > NO_CHECK_EOF
483 483 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
484 484 > from __future__ import absolute_import
485 485 > from extlibroot.shadowing.used import detail
486 486 > NO_CHECK_EOF
487 487 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
488 488 > from __future__ import absolute_import
489 489 > from .used import detail as used
490 490 > NO_CHECK_EOF
491 491
492 492 Setup extension local modules to be imported with "absolute_import"
493 493 feature.
494 494
495 495 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
496 496 $ touch $TESTTMP/absextroot/xsub1/__init__.py
497 497 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
498 498
499 499 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
500 500 > def func():
501 501 > return b"this is absextroot.xsub1.xsub2.called.func()"
502 502 > NO_CHECK_EOF
503 503 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
504 504 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
505 505 > NO_CHECK_EOF
506 506 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
507 507 > detail = b"this is absextroot.xsub1.xsub2.used"
508 508 > NO_CHECK_EOF
509 509
510 510 Setup extension local modules to examine whether demand importing
511 511 works as expected in "level > 1" case.
512 512
513 513 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
514 514 > detail = b"this is absextroot.relimportee"
515 515 > NO_CHECK_EOF
516 516 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
517 517 > from __future__ import absolute_import
518 518 > from mercurial import pycompat
519 519 > from ... import relimportee
520 520 > detail = b"this relimporter imports %r" % (
521 521 > pycompat.bytestr(relimportee.detail))
522 522 > NO_CHECK_EOF
523 523
524 524 Setup modules, which actually import extension local modules at
525 525 runtime.
526 526
527 527 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
528 528 > from __future__ import absolute_import
529 529 >
530 530 > # import extension local modules absolutely (level = 0)
531 531 > from absextroot.xsub1.xsub2 import used, unused
532 532 > from absextroot.xsub1.xsub2.called import func
533 533 >
534 534 > def getresult():
535 535 > result = []
536 536 > result.append(used.detail)
537 537 > result.append(func())
538 538 > return result
539 539 > NO_CHECK_EOF
540 540
541 541 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
542 542 > from __future__ import absolute_import
543 543 >
544 544 > # import extension local modules relatively (level == 1)
545 545 > from .xsub1.xsub2 import used, unused
546 546 > from .xsub1.xsub2.called import func
547 547 >
548 548 > # import a module, which implies "importing with level > 1"
549 549 > from .xsub1.xsub2 import relimporter
550 550 >
551 551 > def getresult():
552 552 > result = []
553 553 > result.append(used.detail)
554 554 > result.append(func())
555 555 > result.append(relimporter.detail)
556 556 > return result
557 557 > NO_CHECK_EOF
558 558
559 559 Setup main procedure of extension.
560 560
561 561 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
562 562 > from __future__ import absolute_import
563 563 > from mercurial import registrar
564 564 > cmdtable = {}
565 565 > command = registrar.command(cmdtable)
566 566 >
567 567 > # "absolute" and "relative" shouldn't be imported before actual
568 568 > # command execution, because (1) they import same modules, and (2)
569 569 > # preceding import (= instantiate "demandmod" object instead of
570 570 > # real "module" object) might hide problem of succeeding import.
571 571 >
572 572 > @command(b'showabsolute', [], norepo=True)
573 573 > def showabsolute(ui, *args, **opts):
574 574 > from absextroot import absolute
575 575 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
576 576 >
577 577 > @command(b'showrelative', [], norepo=True)
578 578 > def showrelative(ui, *args, **opts):
579 579 > from . import relative
580 580 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
581 581 >
582 582 > # import modules from external library
583 583 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
584 584 > from extlibroot.lsub1.lsub2.called import func as lfunc
585 585 > from extlibroot.recursedown import absdetail, legacydetail
586 586 > from extlibroot.shadowing import proxied
587 587 >
588 588 > def uisetup(ui):
589 589 > result = []
590 590 > result.append(lused.detail)
591 591 > result.append(lfunc())
592 592 > result.append(absdetail)
593 593 > result.append(legacydetail)
594 594 > result.append(proxied.detail)
595 595 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
596 596 > NO_CHECK_EOF
597 597
598 598 Examine module importing.
599 599
600 600 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
601 601 LIB: this is extlibroot.lsub1.lsub2.used
602 602 LIB: this is extlibroot.lsub1.lsub2.called.func()
603 603 LIB: this is extlibroot.recursedown.abs.used
604 604 LIB: this is extlibroot.recursedown.legacy.used
605 605 LIB: this is extlibroot.shadowing.used
606 606 ABS: this is absextroot.xsub1.xsub2.used
607 607 ABS: this is absextroot.xsub1.xsub2.called.func()
608 608
609 609 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
610 610 LIB: this is extlibroot.lsub1.lsub2.used
611 611 LIB: this is extlibroot.lsub1.lsub2.called.func()
612 612 LIB: this is extlibroot.recursedown.abs.used
613 613 LIB: this is extlibroot.recursedown.legacy.used
614 614 LIB: this is extlibroot.shadowing.used
615 615 REL: this is absextroot.xsub1.xsub2.used
616 616 REL: this is absextroot.xsub1.xsub2.called.func()
617 617 REL: this relimporter imports 'this is absextroot.relimportee'
618 618
619 619 Examine whether sub-module is imported relatively as expected.
620 620
621 621 See also issue5208 for detail about example case on Python 3.x.
622 622
623 623 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
624 624 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
625 625
626 626 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
627 627 > text = 'notexist.py at root is loaded unintentionally\n'
628 628 > NO_CHECK_EOF
629 629
630 630 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
631 631 > from mercurial import registrar
632 632 > cmdtable = {}
633 633 > command = registrar.command(cmdtable)
634 634 >
635 635 > # demand import avoids failure of importing notexist here, but only on
636 636 > # Python 2.
637 637 > import extlibroot.lsub1.lsub2.notexist
638 638 >
639 639 > @command(b'checkrelativity', [], norepo=True)
640 640 > def checkrelativity(ui, *args, **opts):
641 641 > try:
642 642 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
643 643 > return 1 # unintentional success
644 644 > except ImportError:
645 645 > pass # intentional failure
646 646 > NO_CHECK_EOF
647 647
648 648 Python 3's lazy importer verifies modules exist before returning the lazy
649 649 module stub. Our custom lazy importer for Python 2 always returns a stub.
650 650
651 651 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) || true
652 *** failed to import extension "checkrelativity" from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist'
652 *** failed to import extension "checkrelativity" from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist' (py3 !)
653 653 hg: unknown command 'checkrelativity' (py3 !)
654 654 (use 'hg help' for a list of commands) (py3 !)
655 655
656 656 #endif
657 657
658 658 (Here, module importing tests are finished. Therefore, use other than
659 659 NO_CHECK_* limit mark for heredoc python files, in order to apply
660 660 import-checker.py or so on their contents)
661 661
662 662 Make sure a broken uisetup doesn't globally break hg:
663 663 $ cat > $TESTTMP/baduisetup.py <<EOF
664 664 > def uisetup(ui):
665 665 > 1 / 0
666 666 > EOF
667 667
668 668 Even though the extension fails during uisetup, hg is still basically usable:
669 669 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
670 670 Traceback (most recent call last):
671 671 File "*/mercurial/extensions.py", line *, in _runuisetup (glob) (no-pyoxidizer !)
672 672 File "mercurial.extensions", line *, in _runuisetup (glob) (pyoxidizer !)
673 673 uisetup(ui)
674 674 File "$TESTTMP/baduisetup.py", line 2, in uisetup
675 675 1 / 0
676 676 ZeroDivisionError: * by zero (glob)
677 677 *** failed to set up extension baduisetup: * by zero (glob)
678 678 Mercurial Distributed SCM (version *) (glob)
679 679 (see https://mercurial-scm.org for more information)
680 680
681 681 Copyright (C) 2005-* Olivia Mackall and others (glob)
682 682 This is free software; see the source for copying conditions. There is NO
683 683 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
684 684
685 685 $ cd ..
686 686
687 687 hide outer repo
688 688 $ hg init
689 689
690 690 $ cat > empty.py <<EOF
691 691 > '''empty cmdtable
692 692 > '''
693 693 > cmdtable = {}
694 694 > EOF
695 695 $ emptypath=`pwd`/empty.py
696 696 $ echo "empty = $emptypath" >> $HGRCPATH
697 697 $ hg help empty
698 698 empty extension - empty cmdtable
699 699
700 700 no commands defined
701 701
702 702
703 703 $ echo 'empty = !' >> $HGRCPATH
704 704
705 705 $ cat > debugextension.py <<EOF
706 706 > '''only debugcommands
707 707 > '''
708 708 > from mercurial import registrar
709 709 > cmdtable = {}
710 710 > command = registrar.command(cmdtable)
711 711 > @command(b'debugfoobar', [], b'hg debugfoobar')
712 712 > def debugfoobar(ui, repo, *args, **opts):
713 713 > "yet another debug command"
714 714 > @command(b'foo', [], b'hg foo')
715 715 > def foo(ui, repo, *args, **opts):
716 716 > """yet another foo command
717 717 > This command has been DEPRECATED since forever.
718 718 > """
719 719 > EOF
720 720 $ debugpath=`pwd`/debugextension.py
721 721 $ echo "debugextension = $debugpath" >> $HGRCPATH
722 722
723 723 $ hg help debugextension
724 724 hg debugextensions
725 725
726 726 show information about active extensions
727 727
728 728 options:
729 729
730 730 -T --template TEMPLATE display with template
731 731
732 732 (some details hidden, use --verbose to show complete help)
733 733
734 734
735 735 $ hg --verbose help debugextension
736 736 hg debugextensions
737 737
738 738 show information about active extensions
739 739
740 740 options:
741 741
742 742 -T --template TEMPLATE display with template
743 743
744 744 global options ([+] can be repeated):
745 745
746 746 -R --repository REPO repository root directory or name of overlay bundle
747 747 file
748 748 --cwd DIR change working directory
749 749 -y --noninteractive do not prompt, automatically pick the first choice for
750 750 all prompts
751 751 -q --quiet suppress output
752 752 -v --verbose enable additional output
753 753 --color TYPE when to colorize (boolean, always, auto, never, or
754 754 debug)
755 755 --config CONFIG [+] set/override config option (use 'section.name=value')
756 756 --debug enable debugging output
757 757 --debugger start debugger
758 758 --encoding ENCODE set the charset encoding (default: ascii)
759 759 --encodingmode MODE set the charset encoding mode (default: strict)
760 760 --traceback always print a traceback on exception
761 761 --time time how long the command takes
762 762 --profile print command execution profile
763 763 --version output version information and exit
764 764 -h --help display help and exit
765 765 --hidden consider hidden changesets
766 766 --pager TYPE when to paginate (boolean, always, auto, or never)
767 767 (default: auto)
768 768
769 769
770 770
771 771
772 772
773 773
774 774 $ hg --debug help debugextension
775 775 hg debugextensions
776 776
777 777 show information about active extensions
778 778
779 779 options:
780 780
781 781 -T --template TEMPLATE display with template
782 782
783 783 global options ([+] can be repeated):
784 784
785 785 -R --repository REPO repository root directory or name of overlay bundle
786 786 file
787 787 --cwd DIR change working directory
788 788 -y --noninteractive do not prompt, automatically pick the first choice for
789 789 all prompts
790 790 -q --quiet suppress output
791 791 -v --verbose enable additional output
792 792 --color TYPE when to colorize (boolean, always, auto, never, or
793 793 debug)
794 794 --config CONFIG [+] set/override config option (use 'section.name=value')
795 795 --debug enable debugging output
796 796 --debugger start debugger
797 797 --encoding ENCODE set the charset encoding (default: ascii)
798 798 --encodingmode MODE set the charset encoding mode (default: strict)
799 799 --traceback always print a traceback on exception
800 800 --time time how long the command takes
801 801 --profile print command execution profile
802 802 --version output version information and exit
803 803 -h --help display help and exit
804 804 --hidden consider hidden changesets
805 805 --pager TYPE when to paginate (boolean, always, auto, or never)
806 806 (default: auto)
807 807
808 808
809 809
810 810
811 811
812 812 $ echo 'debugextension = !' >> $HGRCPATH
813 813
814 814 Asking for help about a deprecated extension should do something useful:
815 815
816 816 $ hg help glog
817 817 'glog' is provided by the following extension:
818 818
819 819 graphlog command to view revision graphs from a shell (DEPRECATED)
820 820
821 821 (use 'hg help extensions' for information on enabling extensions)
822 822
823 823 Extension module help vs command help:
824 824
825 825 $ echo 'extdiff =' >> $HGRCPATH
826 826 $ hg help extdiff
827 827 hg extdiff [OPT]... [FILE]...
828 828
829 829 use external program to diff repository (or selected files)
830 830
831 831 Show differences between revisions for the specified files, using an
832 832 external program. The default program used is diff, with default options
833 833 "-Npru".
834 834
835 835 To select a different program, use the -p/--program option. The program
836 836 will be passed the names of two directories to compare, unless the --per-
837 837 file option is specified (see below). To pass additional options to the
838 838 program, use -o/--option. These will be passed before the names of the
839 839 directories or files to compare.
840 840
841 841 The --from, --to, and --change options work the same way they do for 'hg
842 842 diff'.
843 843
844 844 The --per-file option runs the external program repeatedly on each file to
845 845 diff, instead of once on two directories. By default, this happens one by
846 846 one, where the next file diff is open in the external program only once
847 847 the previous external program (for the previous file diff) has exited. If
848 848 the external program has a graphical interface, it can open all the file
849 849 diffs at once instead of one by one. See 'hg help -e extdiff' for
850 850 information about how to tell Mercurial that a given program has a
851 851 graphical interface.
852 852
853 853 The --confirm option will prompt the user before each invocation of the
854 854 external program. It is ignored if --per-file isn't specified.
855 855
856 856 (use 'hg help -e extdiff' to show help for the extdiff extension)
857 857
858 858 options ([+] can be repeated):
859 859
860 860 -p --program CMD comparison program to run
861 861 -o --option OPT [+] pass option to comparison program
862 862 --from REV1 revision to diff from
863 863 --to REV2 revision to diff to
864 864 -c --change REV change made by revision
865 865 --per-file compare each file instead of revision snapshots
866 866 --confirm prompt user before each external program invocation
867 867 --patch compare patches for two revisions
868 868 -I --include PATTERN [+] include names matching the given patterns
869 869 -X --exclude PATTERN [+] exclude names matching the given patterns
870 870 -S --subrepos recurse into subrepositories
871 871
872 872 (some details hidden, use --verbose to show complete help)
873 873
874 874
875 875
876 876
877 877
878 878
879 879
880 880
881 881
882 882
883 883 $ hg help --extension extdiff
884 884 extdiff extension - command to allow external programs to compare revisions
885 885
886 886 The extdiff Mercurial extension allows you to use external programs to compare
887 887 revisions, or revision with working directory. The external diff programs are
888 888 called with a configurable set of options and two non-option arguments: paths
889 889 to directories containing snapshots of files to compare.
890 890
891 891 If there is more than one file being compared and the "child" revision is the
892 892 working directory, any modifications made in the external diff program will be
893 893 copied back to the working directory from the temporary directory.
894 894
895 895 The extdiff extension also allows you to configure new diff commands, so you
896 896 do not need to type 'hg extdiff -p kdiff3' always.
897 897
898 898 [extdiff]
899 899 # add new command that runs GNU diff(1) in 'context diff' mode
900 900 cdiff = gdiff -Nprc5
901 901 ## or the old way:
902 902 #cmd.cdiff = gdiff
903 903 #opts.cdiff = -Nprc5
904 904
905 905 # add new command called meld, runs meld (no need to name twice). If
906 906 # the meld executable is not available, the meld tool in [merge-tools]
907 907 # will be used, if available
908 908 meld =
909 909
910 910 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
911 911 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
912 912 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
913 913 # your .vimrc
914 914 vimdiff = gvim -f "+next" \
915 915 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
916 916
917 917 Tool arguments can include variables that are expanded at runtime:
918 918
919 919 $parent1, $plabel1 - filename, descriptive label of first parent
920 920 $child, $clabel - filename, descriptive label of child revision
921 921 $parent2, $plabel2 - filename, descriptive label of second parent
922 922 $root - repository root
923 923 $parent is an alias for $parent1.
924 924
925 925 The extdiff extension will look in your [diff-tools] and [merge-tools]
926 926 sections for diff tool arguments, when none are specified in [extdiff].
927 927
928 928 [extdiff]
929 929 kdiff3 =
930 930
931 931 [diff-tools]
932 932 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
933 933
934 934 If a program has a graphical interface, it might be interesting to tell
935 935 Mercurial about it. It will prevent the program from being mistakenly used in
936 936 a terminal-only environment (such as an SSH terminal session), and will make
937 937 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
938 938 (if you still want to open file diffs one by one, you can use the --confirm
939 939 option).
940 940
941 941 Declaring that a tool has a graphical interface can be done with the "gui"
942 942 flag next to where "diffargs" are specified:
943 943
944 944 [diff-tools]
945 945 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
946 946 kdiff3.gui = true
947 947
948 948 You can use -I/-X and list of file or directory names like normal 'hg diff'
949 949 command. The extdiff extension makes snapshots of only needed files, so
950 950 running the external diff program will actually be pretty fast (at least
951 951 faster than having to compare the entire tree).
952 952
953 953 list of commands:
954 954
955 955 extdiff use external program to diff repository (or selected files)
956 956
957 957 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
958 958
959 959
960 960
961 961
962 962
963 963
964 964
965 965
966 966
967 967
968 968
969 969
970 970
971 971
972 972
973 973
974 974 $ echo 'extdiff = !' >> $HGRCPATH
975 975
976 976 Test help topic with same name as extension
977 977
978 978 $ cat > multirevs.py <<EOF
979 979 > from mercurial import commands, registrar
980 980 > cmdtable = {}
981 981 > command = registrar.command(cmdtable)
982 982 > """multirevs extension
983 983 > Big multi-line module docstring."""
984 984 > @command(b'multirevs', [], b'ARG', norepo=True)
985 985 > def multirevs(ui, repo, arg, *args, **opts):
986 986 > """multirevs command"""
987 987 > EOF
988 988 $ echo "multirevs = multirevs.py" >> $HGRCPATH
989 989
990 990 $ hg help multirevs | tail
991 991 used):
992 992
993 993 hg update :@
994 994
995 995 - Show diff between tags 1.3 and 1.5 (this works because the first and the
996 996 last revisions of the revset are used):
997 997
998 998 hg diff -r 1.3::1.5
999 999
1000 1000 use 'hg help -c multirevs' to see help for the multirevs command
1001 1001
1002 1002
1003 1003
1004 1004
1005 1005
1006 1006
1007 1007 $ hg help -c multirevs
1008 1008 hg multirevs ARG
1009 1009
1010 1010 multirevs command
1011 1011
1012 1012 (some details hidden, use --verbose to show complete help)
1013 1013
1014 1014
1015 1015
1016 1016 $ hg multirevs
1017 1017 hg multirevs: invalid arguments
1018 1018 hg multirevs ARG
1019 1019
1020 1020 multirevs command
1021 1021
1022 1022 (use 'hg multirevs -h' to show more help)
1023 1023 [10]
1024 1024
1025 1025
1026 1026
1027 1027 $ echo "multirevs = !" >> $HGRCPATH
1028 1028
1029 1029 Issue811: Problem loading extensions twice (by site and by user)
1030 1030
1031 1031 $ cat <<EOF >> $HGRCPATH
1032 1032 > mq =
1033 1033 > strip =
1034 1034 > hgext.mq =
1035 1035 > hgext/mq =
1036 1036 > EOF
1037 1037
1038 1038 Show extensions:
1039 1039 (note that mq force load strip, also checking it's not loaded twice)
1040 1040
1041 1041 #if no-extraextensions
1042 1042 $ hg debugextensions
1043 1043 mq
1044 1044 strip
1045 1045 #endif
1046 1046
1047 1047 For extensions, which name matches one of its commands, help
1048 1048 message should ask '-v -e' to get list of built-in aliases
1049 1049 along with extension help itself
1050 1050
1051 1051 $ mkdir $TESTTMP/d
1052 1052 $ cat > $TESTTMP/d/dodo.py <<EOF
1053 1053 > """
1054 1054 > This is an awesome 'dodo' extension. It does nothing and
1055 1055 > writes 'Foo foo'
1056 1056 > """
1057 1057 > from mercurial import commands, registrar
1058 1058 > cmdtable = {}
1059 1059 > command = registrar.command(cmdtable)
1060 1060 > @command(b'dodo', [], b'hg dodo')
1061 1061 > def dodo(ui, *args, **kwargs):
1062 1062 > """Does nothing"""
1063 1063 > ui.write(b"I do nothing. Yay\\n")
1064 1064 > @command(b'foofoo', [], b'hg foofoo')
1065 1065 > def foofoo(ui, *args, **kwargs):
1066 1066 > """Writes 'Foo foo'"""
1067 1067 > ui.write(b"Foo foo\\n")
1068 1068 > EOF
1069 1069 $ dodopath=$TESTTMP/d/dodo.py
1070 1070
1071 1071 $ echo "dodo = $dodopath" >> $HGRCPATH
1072 1072
1073 1073 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1074 1074 $ hg help -e dodo
1075 1075 dodo extension -
1076 1076
1077 1077 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1078 1078
1079 1079 list of commands:
1080 1080
1081 1081 dodo Does nothing
1082 1082 foofoo Writes 'Foo foo'
1083 1083
1084 1084 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1085 1085
1086 1086 Make sure that '-v -e' prints list of built-in aliases along with
1087 1087 extension help itself
1088 1088 $ hg help -v -e dodo
1089 1089 dodo extension -
1090 1090
1091 1091 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1092 1092
1093 1093 list of commands:
1094 1094
1095 1095 dodo Does nothing
1096 1096 foofoo Writes 'Foo foo'
1097 1097
1098 1098 global options ([+] can be repeated):
1099 1099
1100 1100 -R --repository REPO repository root directory or name of overlay bundle
1101 1101 file
1102 1102 --cwd DIR change working directory
1103 1103 -y --noninteractive do not prompt, automatically pick the first choice for
1104 1104 all prompts
1105 1105 -q --quiet suppress output
1106 1106 -v --verbose enable additional output
1107 1107 --color TYPE when to colorize (boolean, always, auto, never, or
1108 1108 debug)
1109 1109 --config CONFIG [+] set/override config option (use 'section.name=value')
1110 1110 --debug enable debugging output
1111 1111 --debugger start debugger
1112 1112 --encoding ENCODE set the charset encoding (default: ascii)
1113 1113 --encodingmode MODE set the charset encoding mode (default: strict)
1114 1114 --traceback always print a traceback on exception
1115 1115 --time time how long the command takes
1116 1116 --profile print command execution profile
1117 1117 --version output version information and exit
1118 1118 -h --help display help and exit
1119 1119 --hidden consider hidden changesets
1120 1120 --pager TYPE when to paginate (boolean, always, auto, or never)
1121 1121 (default: auto)
1122 1122
1123 1123 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1124 1124 $ hg help -v dodo
1125 1125 hg dodo
1126 1126
1127 1127 Does nothing
1128 1128
1129 1129 (use 'hg help -e dodo' to show help for the dodo extension)
1130 1130
1131 1131 options:
1132 1132
1133 1133 --mq operate on patch repository
1134 1134
1135 1135 global options ([+] can be repeated):
1136 1136
1137 1137 -R --repository REPO repository root directory or name of overlay bundle
1138 1138 file
1139 1139 --cwd DIR change working directory
1140 1140 -y --noninteractive do not prompt, automatically pick the first choice for
1141 1141 all prompts
1142 1142 -q --quiet suppress output
1143 1143 -v --verbose enable additional output
1144 1144 --color TYPE when to colorize (boolean, always, auto, never, or
1145 1145 debug)
1146 1146 --config CONFIG [+] set/override config option (use 'section.name=value')
1147 1147 --debug enable debugging output
1148 1148 --debugger start debugger
1149 1149 --encoding ENCODE set the charset encoding (default: ascii)
1150 1150 --encodingmode MODE set the charset encoding mode (default: strict)
1151 1151 --traceback always print a traceback on exception
1152 1152 --time time how long the command takes
1153 1153 --profile print command execution profile
1154 1154 --version output version information and exit
1155 1155 -h --help display help and exit
1156 1156 --hidden consider hidden changesets
1157 1157 --pager TYPE when to paginate (boolean, always, auto, or never)
1158 1158 (default: auto)
1159 1159
1160 1160 In case when extension name doesn't match any of its commands,
1161 1161 help message should ask for '-v' to get list of built-in aliases
1162 1162 along with extension help
1163 1163 $ cat > $TESTTMP/d/dudu.py <<EOF
1164 1164 > """
1165 1165 > This is an awesome 'dudu' extension. It does something and
1166 1166 > also writes 'Beep beep'
1167 1167 > """
1168 1168 > from mercurial import commands, registrar
1169 1169 > cmdtable = {}
1170 1170 > command = registrar.command(cmdtable)
1171 1171 > @command(b'something', [], b'hg something')
1172 1172 > def something(ui, *args, **kwargs):
1173 1173 > """Does something"""
1174 1174 > ui.write(b"I do something. Yaaay\\n")
1175 1175 > @command(b'beep', [], b'hg beep')
1176 1176 > def beep(ui, *args, **kwargs):
1177 1177 > """Writes 'Beep beep'"""
1178 1178 > ui.write(b"Beep beep\\n")
1179 1179 > EOF
1180 1180 $ dudupath=$TESTTMP/d/dudu.py
1181 1181
1182 1182 $ echo "dudu = $dudupath" >> $HGRCPATH
1183 1183
1184 1184 $ hg help -e dudu
1185 1185 dudu extension -
1186 1186
1187 1187 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1188 1188 beep'
1189 1189
1190 1190 list of commands:
1191 1191
1192 1192 beep Writes 'Beep beep'
1193 1193 something Does something
1194 1194
1195 1195 (use 'hg help -v dudu' to show built-in aliases and global options)
1196 1196
1197 1197 In case when extension name doesn't match any of its commands,
1198 1198 help options '-v' and '-v -e' should be equivalent
1199 1199 $ hg help -v dudu
1200 1200 dudu extension -
1201 1201
1202 1202 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1203 1203 beep'
1204 1204
1205 1205 list of commands:
1206 1206
1207 1207 beep Writes 'Beep beep'
1208 1208 something Does something
1209 1209
1210 1210 global options ([+] can be repeated):
1211 1211
1212 1212 -R --repository REPO repository root directory or name of overlay bundle
1213 1213 file
1214 1214 --cwd DIR change working directory
1215 1215 -y --noninteractive do not prompt, automatically pick the first choice for
1216 1216 all prompts
1217 1217 -q --quiet suppress output
1218 1218 -v --verbose enable additional output
1219 1219 --color TYPE when to colorize (boolean, always, auto, never, or
1220 1220 debug)
1221 1221 --config CONFIG [+] set/override config option (use 'section.name=value')
1222 1222 --debug enable debugging output
1223 1223 --debugger start debugger
1224 1224 --encoding ENCODE set the charset encoding (default: ascii)
1225 1225 --encodingmode MODE set the charset encoding mode (default: strict)
1226 1226 --traceback always print a traceback on exception
1227 1227 --time time how long the command takes
1228 1228 --profile print command execution profile
1229 1229 --version output version information and exit
1230 1230 -h --help display help and exit
1231 1231 --hidden consider hidden changesets
1232 1232 --pager TYPE when to paginate (boolean, always, auto, or never)
1233 1233 (default: auto)
1234 1234
1235 1235 $ hg help -v -e dudu
1236 1236 dudu extension -
1237 1237
1238 1238 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1239 1239 beep'
1240 1240
1241 1241 list of commands:
1242 1242
1243 1243 beep Writes 'Beep beep'
1244 1244 something Does something
1245 1245
1246 1246 global options ([+] can be repeated):
1247 1247
1248 1248 -R --repository REPO repository root directory or name of overlay bundle
1249 1249 file
1250 1250 --cwd DIR change working directory
1251 1251 -y --noninteractive do not prompt, automatically pick the first choice for
1252 1252 all prompts
1253 1253 -q --quiet suppress output
1254 1254 -v --verbose enable additional output
1255 1255 --color TYPE when to colorize (boolean, always, auto, never, or
1256 1256 debug)
1257 1257 --config CONFIG [+] set/override config option (use 'section.name=value')
1258 1258 --debug enable debugging output
1259 1259 --debugger start debugger
1260 1260 --encoding ENCODE set the charset encoding (default: ascii)
1261 1261 --encodingmode MODE set the charset encoding mode (default: strict)
1262 1262 --traceback always print a traceback on exception
1263 1263 --time time how long the command takes
1264 1264 --profile print command execution profile
1265 1265 --version output version information and exit
1266 1266 -h --help display help and exit
1267 1267 --hidden consider hidden changesets
1268 1268 --pager TYPE when to paginate (boolean, always, auto, or never)
1269 1269 (default: auto)
1270 1270
1271 1271 Disabled extension commands:
1272 1272
1273 1273 $ ORGHGRCPATH=$HGRCPATH
1274 1274 $ HGRCPATH=
1275 1275 $ export HGRCPATH
1276 1276 $ hg help email
1277 1277 'email' is provided by the following extension:
1278 1278
1279 1279 patchbomb command to send changesets as (a series of) patch emails
1280 1280
1281 1281 (use 'hg help extensions' for information on enabling extensions)
1282 1282
1283 1283
1284 1284 $ hg qdel
1285 1285 hg: unknown command 'qdel'
1286 1286 'qdelete' is provided by the following extension:
1287 1287
1288 1288 mq manage a stack of patches
1289 1289
1290 1290 (use 'hg help extensions' for information on enabling extensions)
1291 1291 [255]
1292 1292
1293 1293
1294 1294 $ hg churn
1295 1295 hg: unknown command 'churn'
1296 1296 'churn' is provided by the following extension:
1297 1297
1298 1298 churn command to display statistics about repository history
1299 1299
1300 1300 (use 'hg help extensions' for information on enabling extensions)
1301 1301 [255]
1302 1302
1303 1303
1304 1304
1305 1305 Disabled extensions:
1306 1306
1307 1307 $ hg help churn
1308 1308 churn extension - command to display statistics about repository history
1309 1309
1310 1310 (use 'hg help extensions' for information on enabling extensions)
1311 1311
1312 1312 $ hg help patchbomb
1313 1313 patchbomb extension - command to send changesets as (a series of) patch emails
1314 1314
1315 1315 The series is started off with a "[PATCH 0 of N]" introduction, which
1316 1316 describes the series as a whole.
1317 1317
1318 1318 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1319 1319 line of the changeset description as the subject text. The message contains
1320 1320 two or three body parts:
1321 1321
1322 1322 - The changeset description.
1323 1323 - [Optional] The result of running diffstat on the patch.
1324 1324 - The patch itself, as generated by 'hg export'.
1325 1325
1326 1326 Each message refers to the first in the series using the In-Reply-To and
1327 1327 References headers, so they will show up as a sequence in threaded mail and
1328 1328 news readers, and in mail archives.
1329 1329
1330 1330 To configure other defaults, add a section like this to your configuration
1331 1331 file:
1332 1332
1333 1333 [email]
1334 1334 from = My Name <my@email>
1335 1335 to = recipient1, recipient2, ...
1336 1336 cc = cc1, cc2, ...
1337 1337 bcc = bcc1, bcc2, ...
1338 1338 reply-to = address1, address2, ...
1339 1339
1340 1340 Use "[patchbomb]" as configuration section name if you need to override global
1341 1341 "[email]" address settings.
1342 1342
1343 1343 Then you can use the 'hg email' command to mail a series of changesets as a
1344 1344 patchbomb.
1345 1345
1346 1346 You can also either configure the method option in the email section to be a
1347 1347 sendmail compatible mailer or fill out the [smtp] section so that the
1348 1348 patchbomb extension can automatically send patchbombs directly from the
1349 1349 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1350 1350
1351 1351 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1352 1352 supply one via configuration or the command line. You can override this to
1353 1353 never prompt by configuring an empty value:
1354 1354
1355 1355 [email]
1356 1356 cc =
1357 1357
1358 1358 You can control the default inclusion of an introduction message with the
1359 1359 "patchbomb.intro" configuration option. The configuration is always
1360 1360 overwritten by command line flags like --intro and --desc:
1361 1361
1362 1362 [patchbomb]
1363 1363 intro=auto # include introduction message if more than 1 patch (default)
1364 1364 intro=never # never include an introduction message
1365 1365 intro=always # always include an introduction message
1366 1366
1367 1367 You can specify a template for flags to be added in subject prefixes. Flags
1368 1368 specified by --flag option are exported as "{flags}" keyword:
1369 1369
1370 1370 [patchbomb]
1371 1371 flagtemplate = "{separate(' ',
1372 1372 ifeq(branch, 'default', '', branch|upper),
1373 1373 flags)}"
1374 1374
1375 1375 You can set patchbomb to always ask for confirmation by setting
1376 1376 "patchbomb.confirm" to true.
1377 1377
1378 1378 (use 'hg help extensions' for information on enabling extensions)
1379 1379
1380 1380
1381 1381 Help can find unimported extensions
1382 1382 -----------------------------------
1383 1383
1384 1384 XXX-PYOXIDIZER since the frozen binary does not have source directory tree,
1385 1385 this make the checking for actual file under `hgext` a bit complicated. In
1386 1386 addition these tests do some strange dance to ensure some other module are the
1387 1387 first in `sys.path` (since the current install path is always in front
1388 1388 otherwise) that are fragile and that does not match reality in the field. So
1389 1389 for now we disable this test untill a deeper rework of that logic is done.
1390 1390
1391 1391 #if no-pyoxidizer
1392 1392
1393 1393 Broken disabled extension and command:
1394 1394
1395 1395 $ mkdir hgext
1396 1396 $ echo > hgext/__init__.py
1397 1397 $ cat > hgext/broken.py <<NO_CHECK_EOF
1398 1398 > "broken extension'
1399 1399 > NO_CHECK_EOF
1400 1400 $ cat > path.py <<EOF
1401 1401 > import os
1402 1402 > import sys
1403 1403 > sys.path.insert(0, os.environ['HGEXTPATH'])
1404 1404 > EOF
1405 1405 $ HGEXTPATH=`pwd`
1406 1406 $ export HGEXTPATH
1407 1407
1408 1408 $ hg --config extensions.path=./path.py help broken
1409 1409 broken extension - (no help text available)
1410 1410
1411 1411 (use 'hg help extensions' for information on enabling extensions)
1412 1412
1413 1413
1414 1414 $ cat > hgext/forest.py <<EOF
1415 1415 > cmdtable = None
1416 1416 > @command()
1417 1417 > def f():
1418 1418 > pass
1419 1419 > @command(123)
1420 1420 > def g():
1421 1421 > pass
1422 1422 > EOF
1423 1423 $ hg --config extensions.path=./path.py help foo
1424 1424 abort: no such help topic: foo
1425 1425 (try 'hg help --keyword foo')
1426 1426 [255]
1427 1427
1428 1428 #endif
1429 1429
1430 1430 ---
1431 1431
1432 1432 $ cat > throw.py <<EOF
1433 1433 > from mercurial import commands, registrar, util
1434 1434 > cmdtable = {}
1435 1435 > command = registrar.command(cmdtable)
1436 1436 > class Bogon(Exception): pass
1437 1437 > # NB: version should be bytes; simulating extension not ported to py3
1438 1438 > __version__ = '1.0.0'
1439 1439 > @command(b'throw', [], b'hg throw', norepo=True)
1440 1440 > def throw(ui, **opts):
1441 1441 > """throws an exception"""
1442 1442 > raise Bogon()
1443 1443 > EOF
1444 1444
1445 1445 Test extension without proper byteification of key attributes doesn't crash when
1446 1446 accessed.
1447 1447
1448 1448 $ hg version -v --config extensions.throw=throw.py | grep '^ '
1449 1449 throw external 1.0.0
1450 1450
1451 1451 No declared supported version, extension complains:
1452 1452 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1453 1453 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1454 1454 ** which supports versions unknown of Mercurial.
1455 1455 ** Please disable "throw" and try your action again.
1456 1456 ** If that fixes the bug please report it to the extension author.
1457 1457 ** Python * (glob)
1458 1458 ** Mercurial Distributed SCM * (glob)
1459 1459 ** Extensions loaded: throw 1.0.0
1460 1460
1461 1461 empty declaration of supported version, extension complains (but doesn't choke if
1462 1462 the value is improperly a str instead of bytes):
1463 1463 $ echo "testedwith = ''" >> throw.py
1464 1464 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1465 1465 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1466 1466 ** which supports versions unknown of Mercurial.
1467 1467 ** Please disable "throw" and try your action again.
1468 1468 ** If that fixes the bug please report it to the extension author.
1469 1469 ** Python * (glob)
1470 1470 ** Mercurial Distributed SCM (*) (glob)
1471 1471 ** Extensions loaded: throw 1.0.0
1472 1472
1473 1473 If the extension specifies a buglink, show that (but don't choke if the value is
1474 1474 improperly a str instead of bytes):
1475 1475 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1476 1476 $ rm -f throw.pyc throw.pyo
1477 1477 $ rm -Rf __pycache__
1478 1478 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1479 1479 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1480 1480 ** which supports versions unknown of Mercurial.
1481 1481 ** Please disable "throw" and try your action again.
1482 1482 ** If that fixes the bug please report it to http://example.com/bts
1483 1483 ** Python * (glob)
1484 1484 ** Mercurial Distributed SCM (*) (glob)
1485 1485 ** Extensions loaded: throw 1.0.0
1486 1486
1487 1487 If the extensions declare outdated versions, accuse the older extension first:
1488 1488 $ echo "from mercurial import util" >> older.py
1489 1489 $ echo "util.version = lambda:b'2.2'" >> older.py
1490 1490 $ echo "testedwith = b'1.9.3'" >> older.py
1491 1491 $ echo "testedwith = b'2.1.1'" >> throw.py
1492 1492 $ rm -f throw.pyc throw.pyo
1493 1493 $ rm -Rf __pycache__
1494 1494 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1495 1495 > throw 2>&1 | egrep '^\*\*'
1496 1496 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1497 1497 ** which supports versions 1.9 of Mercurial.
1498 1498 ** Please disable "older" and try your action again.
1499 1499 ** If that fixes the bug please report it to the extension author.
1500 1500 ** Python * (glob)
1501 1501 ** Mercurial Distributed SCM (version 2.2)
1502 1502 ** Extensions loaded: older, throw 1.0.0
1503 1503
1504 1504 One extension only tested with older, one only with newer versions:
1505 1505 $ echo "util.version = lambda:b'2.1'" >> older.py
1506 1506 $ rm -f older.pyc older.pyo
1507 1507 $ rm -Rf __pycache__
1508 1508 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1509 1509 > throw 2>&1 | egrep '^\*\*'
1510 1510 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1511 1511 ** which supports versions 1.9 of Mercurial.
1512 1512 ** Please disable "older" and try your action again.
1513 1513 ** If that fixes the bug please report it to the extension author.
1514 1514 ** Python * (glob)
1515 1515 ** Mercurial Distributed SCM (version 2.1)
1516 1516 ** Extensions loaded: older, throw 1.0.0
1517 1517
1518 1518 Older extension is tested with current version, the other only with newer:
1519 1519 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1520 1520 $ rm -f older.pyc older.pyo
1521 1521 $ rm -Rf __pycache__
1522 1522 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1523 1523 > throw 2>&1 | egrep '^\*\*'
1524 1524 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1525 1525 ** which supports versions 2.1 of Mercurial.
1526 1526 ** Please disable "throw" and try your action again.
1527 1527 ** If that fixes the bug please report it to http://example.com/bts
1528 1528 ** Python * (glob)
1529 1529 ** Mercurial Distributed SCM (version 1.9.3)
1530 1530 ** Extensions loaded: older, throw 1.0.0
1531 1531
1532 1532 Ability to point to a different point
1533 1533 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1534 1534 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1535 1535 ** unknown exception encountered, please report by visiting
1536 1536 ** Your Local Goat Lenders
1537 1537 ** Python * (glob)
1538 1538 ** Mercurial Distributed SCM (*) (glob)
1539 1539 ** Extensions loaded: older, throw 1.0.0
1540 1540
1541 1541 Declare the version as supporting this hg version, show regular bts link:
1542 1542 $ hgver=`hg debuginstall -T '{hgver}'`
1543 1543 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1544 1544 $ if [ -z "$hgver" ]; then
1545 1545 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1546 1546 > fi
1547 1547 $ rm -f throw.pyc throw.pyo
1548 1548 $ rm -Rf __pycache__
1549 1549 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1550 1550 ** unknown exception encountered, please report by visiting
1551 1551 ** https://mercurial-scm.org/wiki/BugTracker
1552 1552 ** Python * (glob)
1553 1553 ** Mercurial Distributed SCM (*) (glob)
1554 1554 ** Extensions loaded: throw 1.0.0
1555 1555
1556 1556 Patch version is ignored during compatibility check
1557 1557 $ echo "testedwith = b'3.2'" >> throw.py
1558 1558 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1559 1559 $ rm -f throw.pyc throw.pyo
1560 1560 $ rm -Rf __pycache__
1561 1561 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1562 1562 ** unknown exception encountered, please report by visiting
1563 1563 ** https://mercurial-scm.org/wiki/BugTracker
1564 1564 ** Python * (glob)
1565 1565 ** Mercurial Distributed SCM (*) (glob)
1566 1566 ** Extensions loaded: throw 1.0.0
1567 1567
1568 1568 Test version number support in 'hg version':
1569 1569 $ echo '__version__ = (1, 2, 3)' >> throw.py
1570 1570 $ rm -f throw.pyc throw.pyo
1571 1571 $ rm -Rf __pycache__
1572 1572 $ hg version -v
1573 1573 Mercurial Distributed SCM (version *) (glob)
1574 1574 (see https://mercurial-scm.org for more information)
1575 1575
1576 1576 Copyright (C) 2005-* Olivia Mackall and others (glob)
1577 1577 This is free software; see the source for copying conditions. There is NO
1578 1578 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1579 1579
1580 1580 Enabled extensions:
1581 1581
1582 1582
1583 1583 $ hg version -v --config extensions.throw=throw.py
1584 1584 Mercurial Distributed SCM (version *) (glob)
1585 1585 (see https://mercurial-scm.org for more information)
1586 1586
1587 1587 Copyright (C) 2005-* Olivia Mackall and others (glob)
1588 1588 This is free software; see the source for copying conditions. There is NO
1589 1589 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1590 1590
1591 1591 Enabled extensions:
1592 1592
1593 1593 throw external 1.2.3
1594 1594 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1595 1595 $ rm -f throw.pyc throw.pyo
1596 1596 $ rm -Rf __pycache__
1597 1597 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1598 1598 Mercurial Distributed SCM (version *) (glob)
1599 1599 (see https://mercurial-scm.org for more information)
1600 1600
1601 1601 Copyright (C) 2005-* Olivia Mackall and others (glob)
1602 1602 This is free software; see the source for copying conditions. There is NO
1603 1603 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1604 1604
1605 1605 Enabled extensions:
1606 1606
1607 1607 strip internal
1608 1608 throw external 1.twentythree
1609 1609
1610 1610 $ hg version -q --config extensions.throw=throw.py
1611 1611 Mercurial Distributed SCM (version *) (glob)
1612 1612
1613 1613 Test template output:
1614 1614
1615 1615 $ hg version --config extensions.strip= -T'{extensions}'
1616 1616 strip
1617 1617
1618 1618 Test JSON output of version:
1619 1619
1620 1620 $ hg version -Tjson
1621 1621 [
1622 1622 {
1623 1623 "extensions": [],
1624 1624 "ver": "*" (glob)
1625 1625 }
1626 1626 ]
1627 1627
1628 1628 $ hg version --config extensions.throw=throw.py -Tjson
1629 1629 [
1630 1630 {
1631 1631 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1632 1632 "ver": "3.2.2"
1633 1633 }
1634 1634 ]
1635 1635
1636 1636 $ hg version --config extensions.strip= -Tjson
1637 1637 [
1638 1638 {
1639 1639 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1640 1640 "ver": "*" (glob)
1641 1641 }
1642 1642 ]
1643 1643
1644 1644 Test template output of version:
1645 1645
1646 1646 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1647 1647 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1648 1648 strip (internal)
1649 1649 throw 1.twentythree (external)
1650 1650
1651 1651 Refuse to load extensions with minimum version requirements
1652 1652
1653 1653 $ cat > minversion1.py << EOF
1654 1654 > from mercurial import util
1655 1655 > util.version = lambda: b'3.5.2'
1656 1656 > minimumhgversion = b'3.6'
1657 1657 > EOF
1658 1658 $ hg --config extensions.minversion=minversion1.py version
1659 1659 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1660 1660 Mercurial Distributed SCM (version 3.5.2)
1661 1661 (see https://mercurial-scm.org for more information)
1662 1662
1663 1663 Copyright (C) 2005-* Olivia Mackall and others (glob)
1664 1664 This is free software; see the source for copying conditions. There is NO
1665 1665 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1666 1666
1667 1667 $ cat > minversion2.py << EOF
1668 1668 > from mercurial import util
1669 1669 > util.version = lambda: b'3.6'
1670 1670 > minimumhgversion = b'3.7'
1671 1671 > EOF
1672 1672 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1673 1673 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1674 1674
1675 1675 Can load version that is only off by point release
1676 1676
1677 1677 $ cat > minversion2.py << EOF
1678 1678 > from mercurial import util
1679 1679 > util.version = lambda: b'3.6.1'
1680 1680 > minimumhgversion = b'3.6'
1681 1681 > EOF
1682 1682 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1683 1683 [1]
1684 1684
1685 1685 Can load minimum version identical to current
1686 1686
1687 1687 $ cat > minversion3.py << EOF
1688 1688 > from mercurial import util
1689 1689 > util.version = lambda: b'3.5'
1690 1690 > minimumhgversion = b'3.5'
1691 1691 > EOF
1692 1692 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1693 1693 [1]
1694 1694
1695 1695 Don't explode on py3 with a bad version number (both str vs bytes, and not enough
1696 1696 parts)
1697 1697
1698 1698 $ cat > minversion4.py << EOF
1699 1699 > from mercurial import util
1700 1700 > util.version = lambda: b'3.5'
1701 1701 > minimumhgversion = '3'
1702 1702 > EOF
1703 1703 $ hg --config extensions.minversion=minversion4.py version -v
1704 1704 Mercurial Distributed SCM (version 3.5)
1705 1705 (see https://mercurial-scm.org for more information)
1706 1706
1707 1707 Copyright (C) 2005-* Olivia Mackall and others (glob)
1708 1708 This is free software; see the source for copying conditions. There is NO
1709 1709 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1710 1710
1711 1711 Enabled extensions:
1712 1712
1713 1713 minversion external
1714 1714
1715 1715 Restore HGRCPATH
1716 1716
1717 1717 $ HGRCPATH=$ORGHGRCPATH
1718 1718 $ export HGRCPATH
1719 1719
1720 1720 Commands handling multiple repositories at a time should invoke only
1721 1721 "reposetup()" of extensions enabling in the target repository.
1722 1722
1723 1723 $ mkdir reposetup-test
1724 1724 $ cd reposetup-test
1725 1725
1726 1726 $ cat > $TESTTMP/reposetuptest.py <<EOF
1727 1727 > from mercurial import extensions
1728 1728 > def reposetup(ui, repo):
1729 1729 > ui.write(b'reposetup() for %s\n' % (repo.root))
1730 1730 > ui.flush()
1731 1731 > EOF
1732 1732 $ hg init src
1733 1733 $ echo a > src/a
1734 1734 $ hg -R src commit -Am '#0 at src/a'
1735 1735 adding a
1736 1736 $ echo '[extensions]' >> src/.hg/hgrc
1737 1737 $ echo '# enable extension locally' >> src/.hg/hgrc
1738 1738 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1739 1739 $ hg -R src status
1740 1740 reposetup() for $TESTTMP/reposetup-test/src
1741 1741 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1742 1742
1743 1743 #if no-extraextensions
1744 1744 $ hg --cwd src debugextensions
1745 1745 reposetup() for $TESTTMP/reposetup-test/src
1746 1746 dodo (untested!)
1747 1747 dudu (untested!)
1748 1748 mq
1749 1749 reposetuptest (untested!)
1750 1750 strip
1751 1751 #endif
1752 1752
1753 1753 $ hg clone -U src clone-dst1
1754 1754 reposetup() for $TESTTMP/reposetup-test/src
1755 1755 $ hg init push-dst1
1756 1756 $ hg -q -R src push push-dst1
1757 1757 reposetup() for $TESTTMP/reposetup-test/src
1758 1758 $ hg init pull-src1
1759 1759 $ hg -q -R pull-src1 pull src
1760 1760 reposetup() for $TESTTMP/reposetup-test/src
1761 1761
1762 1762 $ cat <<EOF >> $HGRCPATH
1763 1763 > [extensions]
1764 1764 > # disable extension globally and explicitly
1765 1765 > reposetuptest = !
1766 1766 > EOF
1767 1767 $ hg clone -U src clone-dst2
1768 1768 reposetup() for $TESTTMP/reposetup-test/src
1769 1769 $ hg init push-dst2
1770 1770 $ hg -q -R src push push-dst2
1771 1771 reposetup() for $TESTTMP/reposetup-test/src
1772 1772 $ hg init pull-src2
1773 1773 $ hg -q -R pull-src2 pull src
1774 1774 reposetup() for $TESTTMP/reposetup-test/src
1775 1775
1776 1776 $ cat <<EOF >> $HGRCPATH
1777 1777 > [extensions]
1778 1778 > # enable extension globally
1779 1779 > reposetuptest = $TESTTMP/reposetuptest.py
1780 1780 > EOF
1781 1781 $ hg clone -U src clone-dst3
1782 1782 reposetup() for $TESTTMP/reposetup-test/src
1783 1783 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1784 1784 $ hg init push-dst3
1785 1785 reposetup() for $TESTTMP/reposetup-test/push-dst3
1786 1786 $ hg -q -R src push push-dst3
1787 1787 reposetup() for $TESTTMP/reposetup-test/src
1788 1788 reposetup() for $TESTTMP/reposetup-test/push-dst3
1789 1789 $ hg init pull-src3
1790 1790 reposetup() for $TESTTMP/reposetup-test/pull-src3
1791 1791 $ hg -q -R pull-src3 pull src
1792 1792 reposetup() for $TESTTMP/reposetup-test/pull-src3
1793 1793 reposetup() for $TESTTMP/reposetup-test/src
1794 1794
1795 1795 $ echo '[extensions]' >> src/.hg/hgrc
1796 1796 $ echo '# disable extension locally' >> src/.hg/hgrc
1797 1797 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1798 1798 $ hg clone -U src clone-dst4
1799 1799 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1800 1800 $ hg init push-dst4
1801 1801 reposetup() for $TESTTMP/reposetup-test/push-dst4
1802 1802 $ hg -q -R src push push-dst4
1803 1803 reposetup() for $TESTTMP/reposetup-test/push-dst4
1804 1804 $ hg init pull-src4
1805 1805 reposetup() for $TESTTMP/reposetup-test/pull-src4
1806 1806 $ hg -q -R pull-src4 pull src
1807 1807 reposetup() for $TESTTMP/reposetup-test/pull-src4
1808 1808
1809 1809 disabling in command line overlays with all configuration
1810 1810 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1811 1811 $ hg --config extensions.reposetuptest=! init push-dst5
1812 1812 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1813 1813 $ hg --config extensions.reposetuptest=! init pull-src5
1814 1814 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1815 1815
1816 1816 $ cat <<EOF >> $HGRCPATH
1817 1817 > [extensions]
1818 1818 > # disable extension globally and explicitly
1819 1819 > reposetuptest = !
1820 1820 > EOF
1821 1821 $ hg init parent
1822 1822 $ hg init parent/sub1
1823 1823 $ echo 1 > parent/sub1/1
1824 1824 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1825 1825 adding 1
1826 1826 $ hg init parent/sub2
1827 1827 $ hg init parent/sub2/sub21
1828 1828 $ echo 21 > parent/sub2/sub21/21
1829 1829 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1830 1830 adding 21
1831 1831 $ cat > parent/sub2/.hgsub <<EOF
1832 1832 > sub21 = sub21
1833 1833 > EOF
1834 1834 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1835 1835 adding .hgsub
1836 1836 $ hg init parent/sub3
1837 1837 $ echo 3 > parent/sub3/3
1838 1838 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1839 1839 adding 3
1840 1840 $ cat > parent/.hgsub <<EOF
1841 1841 > sub1 = sub1
1842 1842 > sub2 = sub2
1843 1843 > sub3 = sub3
1844 1844 > EOF
1845 1845 $ hg -R parent commit -Am '#0 at parent'
1846 1846 adding .hgsub
1847 1847 $ echo '[extensions]' >> parent/.hg/hgrc
1848 1848 $ echo '# enable extension locally' >> parent/.hg/hgrc
1849 1849 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1850 1850 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1851 1851 $ hg -R parent status -S -A
1852 1852 reposetup() for $TESTTMP/reposetup-test/parent
1853 1853 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1854 1854 C .hgsub
1855 1855 C .hgsubstate
1856 1856 C sub1/1
1857 1857 C sub2/.hgsub
1858 1858 C sub2/.hgsubstate
1859 1859 C sub2/sub21/21
1860 1860 C sub3/3
1861 1861
1862 1862 $ cd ..
1863 1863
1864 1864 Prohibit registration of commands that don't use @command (issue5137)
1865 1865
1866 1866 $ hg init deprecated
1867 1867 $ cd deprecated
1868 1868
1869 1869 $ cat <<EOF > deprecatedcmd.py
1870 1870 > def deprecatedcmd(repo, ui):
1871 1871 > pass
1872 1872 > cmdtable = {
1873 1873 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1874 1874 > }
1875 1875 > EOF
1876 1876 $ cat <<EOF > .hg/hgrc
1877 1877 > [extensions]
1878 1878 > deprecatedcmd = `pwd`/deprecatedcmd.py
1879 1879 > mq = !
1880 1880 > hgext.mq = !
1881 1881 > hgext/mq = !
1882 1882 > EOF
1883 1883
1884 1884 $ hg deprecatedcmd > /dev/null
1885 1885 *** failed to import extension "deprecatedcmd" from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1886 1886 *** (use @command decorator to register 'deprecatedcmd')
1887 1887 hg: unknown command 'deprecatedcmd'
1888 1888 (use 'hg help' for a list of commands)
1889 1889 [10]
1890 1890
1891 1891 the extension shouldn't be loaded at all so the mq works:
1892 1892
1893 1893 $ hg qseries --config extensions.mq= > /dev/null
1894 1894 *** failed to import extension "deprecatedcmd" from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1895 1895 *** (use @command decorator to register 'deprecatedcmd')
1896 1896
1897 1897 $ cd ..
1898 1898
1899 1899 Test synopsis and docstring extending
1900 1900
1901 1901 $ hg init exthelp
1902 1902 $ cat > exthelp.py <<EOF
1903 1903 > from mercurial import commands, extensions
1904 1904 > def exbookmarks(orig, *args, **opts):
1905 1905 > return orig(*args, **opts)
1906 1906 > def uisetup(ui):
1907 1907 > synopsis = b' GREPME [--foo] [-x]'
1908 1908 > docstring = '''
1909 1909 > GREPME make sure that this is in the help!
1910 1910 > '''
1911 1911 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1912 1912 > synopsis, docstring)
1913 1913 > EOF
1914 1914 $ abspath=`pwd`/exthelp.py
1915 1915 $ echo '[extensions]' >> $HGRCPATH
1916 1916 $ echo "exthelp = $abspath" >> $HGRCPATH
1917 1917 $ cd exthelp
1918 1918 $ hg help bookmarks | grep GREPME
1919 1919 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1920 1920 GREPME make sure that this is in the help!
1921 1921 $ cd ..
1922 1922
1923 1923 Prohibit the use of unicode strings as the default value of options
1924 1924
1925 1925 $ hg init $TESTTMP/opt-unicode-default
1926 1926
1927 1927 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1928 1928 > from __future__ import print_function
1929 1929 > from mercurial import registrar
1930 1930 > cmdtable = {}
1931 1931 > command = registrar.command(cmdtable)
1932 1932 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1933 1933 > def ext(*args, **opts):
1934 1934 > print(opts[b'opt'], flush=True)
1935 1935 > EOF
1936 1936 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1937 1937 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1938 1938 > [extensions]
1939 1939 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1940 1940 > EOF
1941 1941 $ hg -R $TESTTMP/opt-unicode-default dummy
1942 *** failed to import extension "test_unicode_default_value" from $TESTTMP/test_unicode_default_value.py: unicode 'value' found in cmdtable.dummy
1942 *** failed to import extension "test_unicode_default_value" from $TESTTMP/test_unicode_default_value.py: unicode 'value' found in cmdtable.dummy (py3 !)
1943 *** failed to import extension "test_unicode_default_value" from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy (no-py3 !)
1943 1944 *** (use b'' to make it byte string)
1944 1945 hg: unknown command 'dummy'
1945 1946 (did you mean summary?)
1946 1947 [10]
1947 1948
1948 1949 Check the mandatory extension feature
1949 1950 -------------------------------------
1950 1951
1951 1952 $ hg init mandatory-extensions
1952 1953 $ cat > $TESTTMP/mandatory-extensions/.hg/good.py << EOF
1953 1954 > pass
1954 1955 > EOF
1955 1956 $ cat > $TESTTMP/mandatory-extensions/.hg/bad.py << EOF
1956 1957 > raise RuntimeError("babar")
1957 1958 > EOF
1958 1959 $ cat > $TESTTMP/mandatory-extensions/.hg/syntax.py << EOF
1959 1960 > def (
1960 1961 > EOF
1961 1962
1962 1963 Check that the good one load :
1963 1964
1964 1965 $ cat > $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
1965 1966 > [extensions]
1966 1967 > good = $TESTTMP/mandatory-extensions/.hg/good.py
1967 1968 > EOF
1968 1969
1969 1970 $ hg -R mandatory-extensions id
1970 1971 000000000000 tip
1971 1972
1972 1973 Make it mandatory to load
1973 1974
1974 1975 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
1975 1976 > good:required = yes
1976 1977 > EOF
1977 1978
1978 1979 $ hg -R mandatory-extensions id
1979 1980 000000000000 tip
1980 1981
1981 1982 Check that the bad one does not load
1982 1983
1983 1984 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
1984 1985 > bad = $TESTTMP/mandatory-extensions/.hg/bad.py
1985 1986 > EOF
1986 1987
1987 1988 $ hg -R mandatory-extensions id
1988 1989 *** failed to import extension "bad" from $TESTTMP/mandatory-extensions/.hg/bad.py: babar
1989 1990 000000000000 tip
1990 1991
1991 1992 Make it mandatory to load
1992 1993
1993 1994 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
1994 1995 > bad:required = yes
1995 1996 > EOF
1996 1997
1997 1998 $ hg -R mandatory-extensions id
1998 1999 abort: failed to import extension "bad" from $TESTTMP/mandatory-extensions/.hg/bad.py: babar
1999 2000 (loading of this extension was required, see `hg help config.extensions` for details)
2000 2001 [255]
2001 2002
2002 2003 Make it not mandatory to load
2003 2004
2004 2005 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
2005 2006 > bad:required = no
2006 2007 > EOF
2007 2008
2008 2009 $ hg -R mandatory-extensions id
2009 2010 *** failed to import extension "bad" from $TESTTMP/mandatory-extensions/.hg/bad.py: babar
2010 2011 000000000000 tip
2011 2012
2012 2013 Same check with the syntax error one
2013 2014
2014 2015 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
2015 2016 > bad = !
2016 2017 > syntax = $TESTTMP/mandatory-extensions/.hg/syntax.py
2017 2018 > syntax:required = yes
2018 2019 > EOF
2019 2020
2020 2021 $ hg -R mandatory-extensions id
2021 2022 abort: failed to import extension "syntax" from $TESTTMP/mandatory-extensions/.hg/syntax.py: invalid syntax (*syntax.py, line 1) (glob)
2022 2023 (loading of this extension was required, see `hg help config.extensions` for details)
2023 2024 [255]
2024 2025
2025 2026 Same check with a missing one
2026 2027
2027 2028 $ cat >> $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
2028 2029 > syntax = !
2029 2030 > syntax:required =
2030 2031 > missing = foo/bar/baz/I/do/not/exist/
2031 2032 > missing:required = yes
2032 2033 > EOF
2033 2034
2034 2035 $ hg -R mandatory-extensions id
2035 2036 abort: failed to import extension "missing" from foo/bar/baz/I/do/not/exist/: [Errno 2] $ENOENT$: 'foo/bar/baz/I/do/not/exist'
2036 2037 (loading of this extension was required, see `hg help config.extensions` for details)
2037 2038 [255]
2038 2039
2039 2040 Have a "default" setting for the suboption:
2040 2041
2041 2042 $ cat > $TESTTMP/mandatory-extensions/.hg/hgrc << EOF
2042 2043 > [extensions]
2043 2044 > bad = $TESTTMP/mandatory-extensions/.hg/bad.py
2044 2045 > bad:required = no
2045 2046 > good = $TESTTMP/mandatory-extensions/.hg/good.py
2046 2047 > syntax = $TESTTMP/mandatory-extensions/.hg/syntax.py
2047 2048 > *:required = yes
2048 2049 > EOF
2049 2050
2050 2051 $ hg -R mandatory-extensions id
2051 2052 *** failed to import extension "bad" from $TESTTMP/mandatory-extensions/.hg/bad.py: babar
2052 2053 abort: failed to import extension "syntax" from $TESTTMP/mandatory-extensions/.hg/syntax.py: invalid syntax (*syntax.py, line 1) (glob)
2053 2054 (loading of this extension was required, see `hg help config.extensions` for details)
2054 2055 [255]
General Comments 0
You need to be logged in to leave comments. Login now