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