D4509: extensions: add timing for extensions reposetup

lothiraldan (Boris Feld) phabricator at mercurial-scm.org
Sat Sep 8 10:06:55 UTC 2018


lothiraldan created this revision.
Herald added subscribers: mercurial-devel, mjpieters.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D4509

AFFECTED FILES
  mercurial/hg.py
  tests/test-extension-timing.t
  tests/test-extension.t

CHANGE DETAILS

diff --git a/tests/test-extension.t b/tests/test-extension-timing.t
copy from tests/test-extension.t
copy to tests/test-extension-timing.t
--- a/tests/test-extension.t
+++ b/tests/test-extension-timing.t
@@ -40,1716 +40,51 @@
 
   $ echo '[extensions]' >> $HGRCPATH
   $ echo "foobar = $abspath" >> $HGRCPATH
-  $ hg foo
+
+Test extension setup timings
+
+  $ hg foo --traceback --config devel.debug.extensions=yes --debug 2>&1
+  debug.extensions: loading extensions
+  debug.extensions: - processing 1 entries
+  debug.extensions:   - loading extension: 'foobar'
+  debug.extensions:   > 'foobar' extension loaded in * (glob)
+  debug.extensions:     - validating extension tables: 'foobar'
+  debug.extensions:     - invoking registered callbacks: 'foobar'
+  debug.extensions:     > callbacks completed in * (glob)
+  debug.extensions: > loaded 1 extensions, total time * (glob)
+  debug.extensions: - loading configtable attributes
+  debug.extensions: - executing uisetup hooks
+  debug.extensions:   - running uisetup for 'foobar'
+  uisetup called [debug]
   uisetup called
   uisetup called [status]
-  reposetup called for a
-  ui == repo.ui
-  reposetup called for a (chg !)
-  ui == repo.ui (chg !)
-  Foo
-  $ hg foo --quiet
-  uisetup called (no-chg !)
-  reposetup called for a (chg !)
-  ui == repo.ui
-  Foo
-  $ hg foo --debug
-  uisetup called [debug] (no-chg !)
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  reposetup called for a (chg !)
-  ui == repo.ui
-  Foo
-
-  $ cd ..
-  $ hg clone a b
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  reposetup called for a
-  ui == repo.ui
-  reposetup called for b
-  ui == repo.ui
-  updating to branch default
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-
-  $ hg bar
-  uisetup called (no-chg !)
-  uisetup called [status] (no-chg !)
-  Bar
-  $ echo 'foobar = !' >> $HGRCPATH
-
-module/__init__.py-style
-
-  $ echo "barfoo = $barfoopath" >> $HGRCPATH
-  $ cd a
-  $ hg foo
-  uisetup called
-  uisetup called [status]
+  debug.extensions:   > uisetup for 'foobar' took * (glob)
+  debug.extensions: - executing extsetup hooks
+  debug.extensions:   - running extsetup for 'foobar'
+  debug.extensions:   > extsetup for 'foobar' took * (glob)
+  debug.extensions: - executing remaining aftercallbacks
+  debug.extensions: > remaining aftercallbacks completed in * (glob)
+  debug.extensions: - loading extension registration objects
+  debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: extension loading complete
+  debug.extensions: loading additional extensions
+  debug.extensions: - processing 1 entries
+  debug.extensions: > loaded 0 extensions, total time * (glob)
+  debug.extensions: - loading configtable attributes
+  debug.extensions: - executing uisetup hooks
+  debug.extensions: - executing extsetup hooks
+  debug.extensions: - executing remaining aftercallbacks
+  debug.extensions: > remaining aftercallbacks completed in * (glob)
+  debug.extensions: - loading extension registration objects
+  debug.extensions: > extension registration object loading took * (glob)
+  debug.extensions: extension loading complete
+  debug.extensions: - executing reposetup hooks
+  debug.extensions:   - running reposetup for foobar
   reposetup called for a
   ui == repo.ui
-  reposetup called for a (chg !)
-  ui == repo.ui (chg !)
+  debug.extensions:   > reposetup for 'foobar' took * (glob)
   Foo
-  $ echo 'barfoo = !' >> $HGRCPATH
-
-Check that extensions are loaded in phases:
-
-  $ cat > foo.py <<EOF
-  > import os
-  > name = os.path.basename(__file__).rsplit('.', 1)[0]
-  > print("1) %s imported" % name)
-  > def uisetup(ui):
-  >     print("2) %s uisetup" % name)
-  > def extsetup():
-  >     print("3) %s extsetup" % name)
-  > def reposetup(ui, repo):
-  >    print("4) %s reposetup" % name)
-  > 
-  > # custom predicate to check registration of functions at loading
-  > from mercurial import (
-  >     registrar,
-  >     smartset,
-  > )
-  > revsetpredicate = registrar.revsetpredicate()
-  > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
-  > def custompredicate(repo, subset, x):
-  >     return smartset.baseset([r for r in subset if r in {0}])
-  > EOF
-
-  $ cp foo.py bar.py
-  $ echo 'foo = foo.py' >> $HGRCPATH
-  $ echo 'bar = bar.py' >> $HGRCPATH
-
-Check normal command's load order of extensions and registration of functions
-
-  $ hg log -r "foo() and bar()" -q
-  1) foo imported
-  1) bar imported
-  2) foo uisetup
-  2) bar uisetup
-  3) foo extsetup
-  3) bar extsetup
-  4) foo reposetup
-  4) bar reposetup
-  0:c24b9ac61126
-
-Check hgweb's load order of extensions and registration of functions
-
-  $ cat > hgweb.cgi <<EOF
-  > #!$PYTHON
-  > from mercurial import demandimport; demandimport.enable()
-  > from mercurial.hgweb import hgweb
-  > from mercurial.hgweb import wsgicgi
-  > application = hgweb('.', 'test repo')
-  > wsgicgi.launch(application)
-  > EOF
-  $ . "$TESTDIR/cgienv"
-
-  $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
-  >    | grep '^[0-9]) ' # ignores HTML output
-  1) foo imported
-  1) bar imported
-  2) foo uisetup
-  2) bar uisetup
-  3) foo extsetup
-  3) bar extsetup
-  4) foo reposetup
-  4) bar reposetup
-
-(check that revset predicate foo() and bar() are available)
-
-#if msys
-  $ PATH_INFO='//shortlog'
-#else
-  $ PATH_INFO='/shortlog'
-#endif
-  $ export PATH_INFO
-  $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
-  >     | grep '<a href="/rev/[0-9a-z]*">'
-     <a href="/rev/c24b9ac61126">add file</a>
-
-  $ echo 'foo = !' >> $HGRCPATH
-  $ echo 'bar = !' >> $HGRCPATH
-
-Check "from __future__ import absolute_import" support for external libraries
-
-#if windows
-  $ PATHSEP=";"
-#else
-  $ PATHSEP=":"
-#endif
-  $ export PATHSEP
-
-  $ mkdir $TESTTMP/libroot
-  $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
-  $ mkdir $TESTTMP/libroot/mod
-  $ touch $TESTTMP/libroot/mod/__init__.py
-  $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
-
-  $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
-  > from __future__ import absolute_import
-  > import ambig # should load "libroot/ambig.py"
-  > s = ambig.s
-  > EOF
-  $ cat > loadabs.py <<EOF
-  > import mod.ambigabs as ambigabs
-  > def extsetup():
-  >     print('ambigabs.s=%s' % ambigabs.s)
-  > EOF
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
-  ambigabs.s=libroot/ambig.py
-  $TESTTMP/a
-
-#if no-py3k
-  $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
-  > import ambig # should load "libroot/mod/ambig.py"
-  > s = ambig.s
-  > EOF
-  $ cat > loadrel.py <<EOF
-  > import mod.ambigrel as ambigrel
-  > def extsetup():
-  >     print('ambigrel.s=%s' % ambigrel.s)
-  > EOF
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
-  ambigrel.s=libroot/mod/ambig.py
-  $TESTTMP/a
-#endif
-
-Check absolute/relative import of extension specific modules
-
-  $ mkdir $TESTTMP/extroot
-  $ cat > $TESTTMP/extroot/bar.py <<EOF
-  > s = 'this is extroot.bar'
-  > EOF
-  $ mkdir $TESTTMP/extroot/sub1
-  $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
-  > s = 'this is extroot.sub1.__init__'
-  > EOF
-  $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
-  > s = 'this is extroot.sub1.baz'
-  > EOF
-  $ cat > $TESTTMP/extroot/__init__.py <<EOF
-  > s = 'this is extroot.__init__'
-  > import foo
-  > def extsetup(ui):
-  >     ui.write('(extroot) ', foo.func(), '\n')
-  >     ui.flush()
-  > EOF
-
-  $ cat > $TESTTMP/extroot/foo.py <<EOF
-  > # test absolute import
-  > buf = []
-  > def func():
-  >     # "not locals" case
-  >     import extroot.bar
-  >     buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
-  >     return '\n(extroot) '.join(buf)
-  > # "fromlist == ('*',)" case
-  > from extroot.bar import *
-  > buf.append('from extroot.bar import *: %s' % s)
-  > # "not fromlist" and "if '.' in name" case
-  > import extroot.sub1.baz
-  > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
-  > # "not fromlist" and NOT "if '.' in name" case
-  > import extroot
-  > buf.append('import extroot: %s' % extroot.s)
-  > # NOT "not fromlist" and NOT "level != -1" case
-  > from extroot.bar import s
-  > buf.append('from extroot.bar import s: %s' % s)
-  > EOF
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
-  (extroot) from extroot.bar import *: this is extroot.bar
-  (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
-  (extroot) import extroot: this is extroot.__init__
-  (extroot) from extroot.bar import s: this is extroot.bar
-  (extroot) import extroot.bar in func(): this is extroot.bar
-  $TESTTMP/a
-
-#if no-py3k
-  $ rm "$TESTTMP"/extroot/foo.*
-  $ rm -Rf "$TESTTMP/extroot/__pycache__"
-  $ cat > $TESTTMP/extroot/foo.py <<EOF
-  > # test relative import
-  > buf = []
-  > def func():
-  >     # "not locals" case
-  >     import bar
-  >     buf.append('import bar in func(): %s' % bar.s)
-  >     return '\n(extroot) '.join(buf)
-  > # "fromlist == ('*',)" case
-  > from bar import *
-  > buf.append('from bar import *: %s' % s)
-  > # "not fromlist" and "if '.' in name" case
-  > import sub1.baz
-  > buf.append('import sub1.baz: %s' % sub1.baz.s)
-  > # "not fromlist" and NOT "if '.' in name" case
-  > import sub1
-  > buf.append('import sub1: %s' % sub1.s)
-  > # NOT "not fromlist" and NOT "level != -1" case
-  > from bar import s
-  > buf.append('from bar import s: %s' % s)
-  > EOF
-  $ hg --config extensions.extroot=$TESTTMP/extroot root
-  (extroot) from bar import *: this is extroot.bar
-  (extroot) import sub1.baz: this is extroot.sub1.baz
-  (extroot) import sub1: this is extroot.sub1.__init__
-  (extroot) from bar import s: this is extroot.bar
-  (extroot) import bar in func(): this is extroot.bar
-  $TESTTMP/a
-#endif
-
-#if demandimport
-
-Examine whether module loading is delayed until actual referring, even
-though module is imported with "absolute_import" feature.
-
-Files below in each packages are used for described purpose:
-
-- "called": examine whether "from MODULE import ATTR" works correctly
-- "unused": examine whether loading is delayed correctly
-- "used":   examine whether "from PACKAGE import MODULE" works correctly
-
-Package hierarchy is needed to examine whether demand importing works
-as expected for "from SUB.PACK.AGE import MODULE".
-
-Setup "external library" to be imported with "absolute_import"
-feature.
-
-  $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
-  $ touch $TESTTMP/extlibroot/__init__.py
-  $ touch $TESTTMP/extlibroot/lsub1/__init__.py
-  $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
-
-  $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
-  > def func():
-  >     return "this is extlibroot.lsub1.lsub2.called.func()"
-  > EOF
-  $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
-  > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
-  > EOF
-  $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
-  > detail = "this is extlibroot.lsub1.lsub2.used"
-  > EOF
-
-Setup sub-package of "external library", which causes instantiation of
-demandmod in "recurse down the module chain" code path. Relative
-importing with "absolute_import" feature isn't tested, because "level
->=1 " doesn't cause instantiation of demandmod.
-
-  $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
-  $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
-  > detail = "this is extlibroot.recursedown.abs.used"
-  > EOF
-  $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
-  > from __future__ import absolute_import
-  > from extlibroot.recursedown.abs.used import detail
-  > EOF
-
-  $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
-  $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
-  > detail = "this is extlibroot.recursedown.legacy.used"
-  > EOF
-  $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
-  > # legacy style (level == -1) import
-  > from extlibroot.recursedown.legacy.used import detail
-  > EOF
-
-  $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
-  > from __future__ import absolute_import
-  > from extlibroot.recursedown.abs import detail as absdetail
-  > from .legacy import detail as legacydetail
-  > EOF
-
-Setup package that re-exports an attribute of its submodule as the same
-name. This leaves 'shadowing.used' pointing to 'used.detail', but still
-the submodule 'used' should be somehow accessible. (issue5617)
-
-  $ mkdir -p $TESTTMP/extlibroot/shadowing
-  $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
-  > detail = "this is extlibroot.shadowing.used"
-  > EOF
-  $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
-  > from __future__ import absolute_import
-  > from extlibroot.shadowing.used import detail
-  > EOF
-  $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
-  > from __future__ import absolute_import
-  > from .used import detail as used
-  > EOF
-
-Setup extension local modules to be imported with "absolute_import"
-feature.
-
-  $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
-  $ touch $TESTTMP/absextroot/xsub1/__init__.py
-  $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
-
-  $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
-  > def func():
-  >     return "this is absextroot.xsub1.xsub2.called.func()"
-  > EOF
-  $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
-  > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
-  > EOF
-  $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
-  > detail = "this is absextroot.xsub1.xsub2.used"
-  > EOF
-
-Setup extension local modules to examine whether demand importing
-works as expected in "level > 1" case.
-
-  $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
-  > detail = "this is absextroot.relimportee"
-  > EOF
-  $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
-  > from __future__ import absolute_import
-  > from ... import relimportee
-  > detail = "this relimporter imports %r" % (relimportee.detail)
-  > EOF
-
-Setup modules, which actually import extension local modules at
-runtime.
-
-  $ cat > $TESTTMP/absextroot/absolute.py << EOF
-  > from __future__ import absolute_import
-  > 
-  > # import extension local modules absolutely (level = 0)
-  > from absextroot.xsub1.xsub2 import used, unused
-  > from absextroot.xsub1.xsub2.called import func
-  > 
-  > def getresult():
-  >     result = []
-  >     result.append(used.detail)
-  >     result.append(func())
-  >     return result
-  > EOF
-
-  $ cat > $TESTTMP/absextroot/relative.py << EOF
-  > from __future__ import absolute_import
-  > 
-  > # import extension local modules relatively (level == 1)
-  > from .xsub1.xsub2 import used, unused
-  > from .xsub1.xsub2.called import func
-  > 
-  > # import a module, which implies "importing with level > 1"
-  > from .xsub1.xsub2 import relimporter
-  > 
-  > def getresult():
-  >     result = []
-  >     result.append(used.detail)
-  >     result.append(func())
-  >     result.append(relimporter.detail)
-  >     return result
-  > EOF
-
-Setup main procedure of extension.
-
-  $ cat > $TESTTMP/absextroot/__init__.py <<EOF
-  > from __future__ import absolute_import
-  > from mercurial import registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > 
-  > # "absolute" and "relative" shouldn't be imported before actual
-  > # command execution, because (1) they import same modules, and (2)
-  > # preceding import (= instantiate "demandmod" object instead of
-  > # real "module" object) might hide problem of succeeding import.
-  > 
-  > @command(b'showabsolute', [], norepo=True)
-  > def showabsolute(ui, *args, **opts):
-  >     from absextroot import absolute
-  >     ui.write(b'ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
-  > 
-  > @command(b'showrelative', [], norepo=True)
-  > def showrelative(ui, *args, **opts):
-  >     from . import relative
-  >     ui.write(b'REL: %s\n' % '\nREL: '.join(relative.getresult()))
-  > 
-  > # import modules from external library
-  > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
-  > from extlibroot.lsub1.lsub2.called import func as lfunc
-  > from extlibroot.recursedown import absdetail, legacydetail
-  > from extlibroot.shadowing import proxied
-  > 
-  > def uisetup(ui):
-  >     result = []
-  >     result.append(lused.detail)
-  >     result.append(lfunc())
-  >     result.append(absdetail)
-  >     result.append(legacydetail)
-  >     result.append(proxied.detail)
-  >     ui.write(b'LIB: %s\n' % '\nLIB: '.join(result))
-  > EOF
-
-Examine module importing.
-
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
-  LIB: this is extlibroot.lsub1.lsub2.used
-  LIB: this is extlibroot.lsub1.lsub2.called.func()
-  LIB: this is extlibroot.recursedown.abs.used
-  LIB: this is extlibroot.recursedown.legacy.used
-  LIB: this is extlibroot.shadowing.used
-  ABS: this is absextroot.xsub1.xsub2.used
-  ABS: this is absextroot.xsub1.xsub2.called.func()
-
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
-  LIB: this is extlibroot.lsub1.lsub2.used
-  LIB: this is extlibroot.lsub1.lsub2.called.func()
-  LIB: this is extlibroot.recursedown.abs.used
-  LIB: this is extlibroot.recursedown.legacy.used
-  LIB: this is extlibroot.shadowing.used
-  REL: this is absextroot.xsub1.xsub2.used
-  REL: this is absextroot.xsub1.xsub2.called.func()
-  REL: this relimporter imports 'this is absextroot.relimportee'
-
-Examine whether sub-module is imported relatively as expected.
-
-See also issue5208 for detail about example case on Python 3.x.
-
-  $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
-  $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
-
-  $ cat > $TESTTMP/notexist.py <<EOF
-  > text = 'notexist.py at root is loaded unintentionally\n'
-  > EOF
-
-  $ cat > $TESTTMP/checkrelativity.py <<EOF
-  > from mercurial import registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > 
-  > # demand import avoids failure of importing notexist here
-  > import extlibroot.lsub1.lsub2.notexist
-  > 
-  > @command(b'checkrelativity', [], norepo=True)
-  > def checkrelativity(ui, *args, **opts):
-  >     try:
-  >         ui.write(extlibroot.lsub1.lsub2.notexist.text)
-  >         return 1 # unintentional success
-  >     except ImportError:
-  >         pass # intentional failure
-  > EOF
-
-  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
-
-#endif
-
-Make sure a broken uisetup doesn't globally break hg:
-  $ cat > $TESTTMP/baduisetup.py <<EOF
-  > def uisetup(ui):
-  >     1/0
-  > EOF
-
-Even though the extension fails during uisetup, hg is still basically usable:
-  $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
-  Traceback (most recent call last):
-    File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
-      uisetup(ui)
-    File "$TESTTMP/baduisetup.py", line 2, in uisetup
-      1/0
-  ZeroDivisionError: integer division or modulo by zero
-  *** failed to set up extension baduisetup: integer division or modulo by zero
-  Mercurial Distributed SCM (version *) (glob)
-  (see https://mercurial-scm.org for more information)
-  
-  Copyright (C) 2005-* Matt Mackall and others (glob)
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
   $ cd ..
 
-hide outer repo
-  $ hg init
-
-  $ cat > empty.py <<EOF
-  > '''empty cmdtable
-  > '''
-  > cmdtable = {}
-  > EOF
-  $ emptypath=`pwd`/empty.py
-  $ echo "empty = $emptypath" >> $HGRCPATH
-  $ hg help empty
-  empty extension - empty cmdtable
-  
-  no commands defined
-
-
-  $ echo 'empty = !' >> $HGRCPATH
-
-  $ cat > debugextension.py <<EOF
-  > '''only debugcommands
-  > '''
-  > from mercurial import registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > @command(b'debugfoobar', [], b'hg debugfoobar')
-  > def debugfoobar(ui, repo, *args, **opts):
-  >     "yet another debug command"
-  >     pass
-  > @command(b'foo', [], b'hg foo')
-  > def foo(ui, repo, *args, **opts):
-  >     """yet another foo command
-  >     This command has been DEPRECATED since forever.
-  >     """
-  >     pass
-  > EOF
-  $ debugpath=`pwd`/debugextension.py
-  $ echo "debugextension = $debugpath" >> $HGRCPATH
-
-  $ hg help debugextension
-  hg debugextensions
-  
-  show information about active extensions
-  
-  options:
-  
-  (some details hidden, use --verbose to show complete help)
-
-
-  $ hg --verbose help debugextension
-  hg debugextensions
-  
-  show information about active extensions
-  
-  options:
-  
-   -T --template TEMPLATE display with template (EXPERIMENTAL)
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-
-
-
-
-
-  $ hg --debug help debugextension
-  hg debugextensions
-  
-  show information about active extensions
-  
-  options:
-  
-   -T --template TEMPLATE display with template (EXPERIMENTAL)
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-
-
-
-
-  $ echo 'debugextension = !' >> $HGRCPATH
-
-Asking for help about a deprecated extension should do something useful:
-
-  $ hg help glog
-  'glog' is provided by the following extension:
-  
-      graphlog      command to view revision graphs from a shell (DEPRECATED)
-  
-  (use 'hg help extensions' for information on enabling extensions)
-
-Extension module help vs command help:
-
-  $ echo 'extdiff =' >> $HGRCPATH
-  $ hg help extdiff
-  hg extdiff [OPT]... [FILE]...
-  
-  use external program to diff repository (or selected files)
-  
-      Show differences between revisions for the specified files, using an
-      external program. The default program used is diff, with default options
-      "-Npru".
-  
-      To select a different program, use the -p/--program option. The program
-      will be passed the names of two directories to compare. To pass additional
-      options to the program, use -o/--option. These will be passed before the
-      names of the directories to compare.
-  
-      When two revision arguments are given, then changes are shown between
-      those revisions. If only one revision is specified then that revision is
-      compared to the working directory, and, when no revisions are specified,
-      the working directory files are compared to its parent.
-  
-  (use 'hg help -e extdiff' to show help for the extdiff extension)
-  
-  options ([+] can be repeated):
-  
-   -p --program CMD         comparison program to run
-   -o --option OPT [+]      pass option to comparison program
-   -r --rev REV [+]         revision
-   -c --change REV          change made by revision
-      --patch               compare patches for two revisions
-   -I --include PATTERN [+] include names matching the given patterns
-   -X --exclude PATTERN [+] exclude names matching the given patterns
-   -S --subrepos            recurse into subrepositories
-  
-  (some details hidden, use --verbose to show complete help)
-
-
-
-
-
-
-
-
-
-
-  $ hg help --extension extdiff
-  extdiff extension - command to allow external programs to compare revisions
-  
-  The extdiff Mercurial extension allows you to use external programs to compare
-  revisions, or revision with working directory. The external diff programs are
-  called with a configurable set of options and two non-option arguments: paths
-  to directories containing snapshots of files to compare.
-  
-  If there is more than one file being compared and the "child" revision is the
-  working directory, any modifications made in the external diff program will be
-  copied back to the working directory from the temporary directory.
-  
-  The extdiff extension also allows you to configure new diff commands, so you
-  do not need to type 'hg extdiff -p kdiff3' always.
-  
-    [extdiff]
-    # add new command that runs GNU diff(1) in 'context diff' mode
-    cdiff = gdiff -Nprc5
-    ## or the old way:
-    #cmd.cdiff = gdiff
-    #opts.cdiff = -Nprc5
-  
-    # add new command called meld, runs meld (no need to name twice).  If
-    # the meld executable is not available, the meld tool in [merge-tools]
-    # will be used, if available
-    meld =
-  
-    # add new command called vimdiff, runs gvimdiff with DirDiff plugin
-    # (see http://www.vim.org/scripts/script.php?script_id=102) Non
-    # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
-    # your .vimrc
-    vimdiff = gvim -f "+next" \
-              "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
-  
-  Tool arguments can include variables that are expanded at runtime:
-  
-    $parent1, $plabel1 - filename, descriptive label of first parent
-    $child,   $clabel  - filename, descriptive label of child revision
-    $parent2, $plabel2 - filename, descriptive label of second parent
-    $root              - repository root
-    $parent is an alias for $parent1.
-  
-  The extdiff extension will look in your [diff-tools] and [merge-tools]
-  sections for diff tool arguments, when none are specified in [extdiff].
-  
-    [extdiff]
-    kdiff3 =
-  
-    [diff-tools]
-    kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
-  
-  You can use -I/-X and list of file or directory names like normal 'hg diff'
-  command. The extdiff extension makes snapshots of only needed files, so
-  running the external diff program will actually be pretty fast (at least
-  faster than having to compare the entire tree).
-  
-  list of commands:
-  
-   extdiff       use external program to diff repository (or selected files)
-  
-  (use 'hg help -v -e extdiff' to show built-in aliases and global options)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  $ echo 'extdiff = !' >> $HGRCPATH
-
-Test help topic with same name as extension
-
-  $ cat > multirevs.py <<EOF
-  > from mercurial import commands, registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > """multirevs extension
-  > Big multi-line module docstring."""
-  > @command(b'multirevs', [], b'ARG', norepo=True)
-  > def multirevs(ui, repo, arg, *args, **opts):
-  >     """multirevs command"""
-  >     pass
-  > EOF
-  $ echo "multirevs = multirevs.py" >> $HGRCPATH
-
-  $ hg help multirevs | tail
-        used):
-  
-          hg update :@
-  
-      - Show diff between tags 1.3 and 1.5 (this works because the first and the
-        last revisions of the revset are used):
-  
-          hg diff -r 1.3::1.5
-  
-  use 'hg help -c multirevs' to see help for the multirevs command
-
-
-
-
-
-
-  $ hg help -c multirevs
-  hg multirevs ARG
-  
-  multirevs command
-  
-  (some details hidden, use --verbose to show complete help)
-
-
-
-  $ hg multirevs
-  hg multirevs: invalid arguments
-  hg multirevs ARG
-  
-  multirevs command
-  
-  (use 'hg multirevs -h' to show more help)
-  [255]
-
-
-
-  $ echo "multirevs = !" >> $HGRCPATH
-
-Issue811: Problem loading extensions twice (by site and by user)
-
-  $ cat <<EOF >> $HGRCPATH
-  > mq =
-  > strip =
-  > hgext.mq =
-  > hgext/mq =
-  > EOF
-
-Show extensions:
-(note that mq force load strip, also checking it's not loaded twice)
-
-#if no-extraextensions
-  $ hg debugextensions
-  mq
-  strip
-#endif
-
-For extensions, which name matches one of its commands, help
-message should ask '-v -e' to get list of built-in aliases
-along with extension help itself
-
-  $ mkdir $TESTTMP/d
-  $ cat > $TESTTMP/d/dodo.py <<EOF
-  > """
-  > This is an awesome 'dodo' extension. It does nothing and
-  > writes 'Foo foo'
-  > """
-  > from mercurial import commands, registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > @command(b'dodo', [], b'hg dodo')
-  > def dodo(ui, *args, **kwargs):
-  >     """Does nothing"""
-  >     ui.write(b"I do nothing. Yay\\n")
-  > @command(b'foofoo', [], b'hg foofoo')
-  > def foofoo(ui, *args, **kwargs):
-  >     """Writes 'Foo foo'"""
-  >     ui.write(b"Foo foo\\n")
-  > EOF
-  $ dodopath=$TESTTMP/d/dodo.py
-
-  $ echo "dodo = $dodopath" >> $HGRCPATH
-
-Make sure that user is asked to enter '-v -e' to get list of built-in aliases
-  $ hg help -e dodo
-  dodo extension -
-  
-  This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
-  
-  list of commands:
-  
-   dodo          Does nothing
-   foofoo        Writes 'Foo foo'
-  
-  (use 'hg help -v -e dodo' to show built-in aliases and global options)
-
-Make sure that '-v -e' prints list of built-in aliases along with
-extension help itself
-  $ hg help -v -e dodo
-  dodo extension -
-  
-  This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
-  
-  list of commands:
-  
-   dodo          Does nothing
-   foofoo        Writes 'Foo foo'
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-Make sure that single '-v' option shows help and built-ins only for 'dodo' command
-  $ hg help -v dodo
-  hg dodo
-  
-  Does nothing
-  
-  (use 'hg help -e dodo' to show help for the dodo extension)
-  
-  options:
-  
-    --mq operate on patch repository
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-In case when extension name doesn't match any of its commands,
-help message should ask for '-v' to get list of built-in aliases
-along with extension help
-  $ cat > $TESTTMP/d/dudu.py <<EOF
-  > """
-  > This is an awesome 'dudu' extension. It does something and
-  > also writes 'Beep beep'
-  > """
-  > from mercurial import commands, registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > @command(b'something', [], b'hg something')
-  > def something(ui, *args, **kwargs):
-  >     """Does something"""
-  >     ui.write(b"I do something. Yaaay\\n")
-  > @command(b'beep', [], b'hg beep')
-  > def beep(ui, *args, **kwargs):
-  >     """Writes 'Beep beep'"""
-  >     ui.write(b"Beep beep\\n")
-  > EOF
-  $ dudupath=$TESTTMP/d/dudu.py
-
-  $ echo "dudu = $dudupath" >> $HGRCPATH
-
-  $ hg help -e dudu
-  dudu extension -
-  
-  This is an awesome 'dudu' extension. It does something and also writes 'Beep
-  beep'
-  
-  list of commands:
-  
-   beep          Writes 'Beep beep'
-   something     Does something
-  
-  (use 'hg help -v dudu' to show built-in aliases and global options)
-
-In case when extension name doesn't match any of its commands,
-help options '-v' and '-v -e' should be equivalent
-  $ hg help -v dudu
-  dudu extension -
-  
-  This is an awesome 'dudu' extension. It does something and also writes 'Beep
-  beep'
-  
-  list of commands:
-  
-   beep          Writes 'Beep beep'
-   something     Does something
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-  $ hg help -v -e dudu
-  dudu extension -
-  
-  This is an awesome 'dudu' extension. It does something and also writes 'Beep
-  beep'
-  
-  list of commands:
-  
-   beep          Writes 'Beep beep'
-   something     Does something
-  
-  global options ([+] can be repeated):
-  
-   -R --repository REPO   repository root directory or name of overlay bundle
-                          file
-      --cwd DIR           change working directory
-   -y --noninteractive    do not prompt, automatically pick the first choice for
-                          all prompts
-   -q --quiet             suppress output
-   -v --verbose           enable additional output
-      --color TYPE        when to colorize (boolean, always, auto, never, or
-                          debug)
-      --config CONFIG [+] set/override config option (use 'section.name=value')
-      --debug             enable debugging output
-      --debugger          start debugger
-      --encoding ENCODE   set the charset encoding (default: ascii)
-      --encodingmode MODE set the charset encoding mode (default: strict)
-      --traceback         always print a traceback on exception
-      --time              time how long the command takes
-      --profile           print command execution profile
-      --version           output version information and exit
-   -h --help              display help and exit
-      --hidden            consider hidden changesets
-      --pager TYPE        when to paginate (boolean, always, auto, or never)
-                          (default: auto)
-
-Disabled extension commands:
-
-  $ ORGHGRCPATH=$HGRCPATH
-  $ HGRCPATH=
-  $ export HGRCPATH
-  $ hg help email
-  'email' is provided by the following extension:
-  
-      patchbomb     command to send changesets as (a series of) patch emails
-  
-  (use 'hg help extensions' for information on enabling extensions)
-
-
-  $ hg qdel
-  hg: unknown command 'qdel'
-  'qdelete' is provided by the following extension:
-  
-      mq            manage a stack of patches
-  
-  (use 'hg help extensions' for information on enabling extensions)
-  [255]
-
-
-  $ hg churn
-  hg: unknown command 'churn'
-  'churn' is provided by the following extension:
-  
-      churn         command to display statistics about repository history
-  
-  (use 'hg help extensions' for information on enabling extensions)
-  [255]
-
-
-
-Disabled extensions:
-
-  $ hg help churn
-  churn extension - command to display statistics about repository history
-  
-  (use 'hg help extensions' for information on enabling extensions)
-
-  $ hg help patchbomb
-  patchbomb extension - command to send changesets as (a series of) patch emails
-  
-  The series is started off with a "[PATCH 0 of N]" introduction, which
-  describes the series as a whole.
-  
-  Each patch email has a Subject line of "[PATCH M of N] ...", using the first
-  line of the changeset description as the subject text. The message contains
-  two or three body parts:
-  
-  - The changeset description.
-  - [Optional] The result of running diffstat on the patch.
-  - The patch itself, as generated by 'hg export'.
-  
-  Each message refers to the first in the series using the In-Reply-To and
-  References headers, so they will show up as a sequence in threaded mail and
-  news readers, and in mail archives.
-  
-  To configure other defaults, add a section like this to your configuration
-  file:
-  
-    [email]
-    from = My Name <my at email>
-    to = recipient1, recipient2, ...
-    cc = cc1, cc2, ...
-    bcc = bcc1, bcc2, ...
-    reply-to = address1, address2, ...
-  
-  Use "[patchbomb]" as configuration section name if you need to override global
-  "[email]" address settings.
-  
-  Then you can use the 'hg email' command to mail a series of changesets as a
-  patchbomb.
-  
-  You can also either configure the method option in the email section to be a
-  sendmail compatible mailer or fill out the [smtp] section so that the
-  patchbomb extension can automatically send patchbombs directly from the
-  commandline. See the [email] and [smtp] sections in hgrc(5) for details.
-  
-  By default, 'hg email' will prompt for a "To" or "CC" header if you do not
-  supply one via configuration or the command line.  You can override this to
-  never prompt by configuring an empty value:
-  
-    [email]
-    cc =
-  
-  You can control the default inclusion of an introduction message with the
-  "patchbomb.intro" configuration option. The configuration is always
-  overwritten by command line flags like --intro and --desc:
-  
-    [patchbomb]
-    intro=auto   # include introduction message if more than 1 patch (default)
-    intro=never  # never include an introduction message
-    intro=always # always include an introduction message
-  
-  You can specify a template for flags to be added in subject prefixes. Flags
-  specified by --flag option are exported as "{flags}" keyword:
-  
-    [patchbomb]
-    flagtemplate = "{separate(' ',
-                              ifeq(branch, 'default', '', branch|upper),
-                              flags)}"
-  
-  You can set patchbomb to always ask for confirmation by setting
-  "patchbomb.confirm" to true.
-  
-  (use 'hg help extensions' for information on enabling extensions)
-
-
-Broken disabled extension and command:
-
-  $ mkdir hgext
-  $ echo > hgext/__init__.py
-  $ cat > hgext/broken.py <<EOF
-  > "broken extension'
-  > EOF
-  $ cat > path.py <<EOF
-  > import os, sys
-  > sys.path.insert(0, os.environ['HGEXTPATH'])
-  > EOF
-  $ HGEXTPATH=`pwd`
-  $ export HGEXTPATH
-
-  $ hg --config extensions.path=./path.py help broken
-  broken extension - (no help text available)
-  
-  (use 'hg help extensions' for information on enabling extensions)
-
-
-  $ cat > hgext/forest.py <<EOF
-  > cmdtable = None
-  > @command()
-  > def f():
-  >     pass
-  > @command(123)
-  > def g():
-  >     pass
-  > EOF
-  $ hg --config extensions.path=./path.py help foo
-  abort: no such help topic: foo
-  (try 'hg help --keyword foo')
-  [255]
-
-  $ cat > throw.py <<EOF
-  > from mercurial import commands, registrar, util
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > class Bogon(Exception): pass
-  > @command(b'throw', [], b'hg throw', norepo=True)
-  > def throw(ui, **opts):
-  >     """throws an exception"""
-  >     raise Bogon()
-  > EOF
-
-No declared supported version, extension complains:
-  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension throw
-  ** which supports versions unknown of Mercurial.
-  ** Please disable throw and try your action again.
-  ** If that fixes the bug please report it to the extension author.
-  ** Python * (glob)
-  ** Mercurial Distributed SCM * (glob)
-  ** Extensions loaded: throw
-
-empty declaration of supported version, extension complains:
-  $ echo "testedwith = ''" >> throw.py
-  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension throw
-  ** which supports versions unknown of Mercurial.
-  ** Please disable throw and try your action again.
-  ** If that fixes the bug please report it to the extension author.
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (*) (glob)
-  ** Extensions loaded: throw
-
-If the extension specifies a buglink, show that:
-  $ echo 'buglink = "http://example.com/bts"' >> throw.py
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension throw
-  ** which supports versions unknown of Mercurial.
-  ** Please disable throw and try your action again.
-  ** If that fixes the bug please report it to http://example.com/bts
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (*) (glob)
-  ** Extensions loaded: throw
-
-If the extensions declare outdated versions, accuse the older extension first:
-  $ echo "from mercurial import util" >> older.py
-  $ echo "util.version = lambda:b'2.2'" >> older.py
-  $ echo "testedwith = b'1.9.3'" >> older.py
-  $ echo "testedwith = b'2.1.1'" >> throw.py
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
-  >   throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension older
-  ** which supports versions 1.9 of Mercurial.
-  ** Please disable older and try your action again.
-  ** If that fixes the bug please report it to the extension author.
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (version 2.2)
-  ** Extensions loaded: throw, older
-
-One extension only tested with older, one only with newer versions:
-  $ echo "util.version = lambda:b'2.1'" >> older.py
-  $ rm -f older.pyc older.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
-  >   throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension older
-  ** which supports versions 1.9 of Mercurial.
-  ** Please disable older and try your action again.
-  ** If that fixes the bug please report it to the extension author.
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (version 2.1)
-  ** Extensions loaded: throw, older
-
-Older extension is tested with current version, the other only with newer:
-  $ echo "util.version = lambda:b'1.9.3'" >> older.py
-  $ rm -f older.pyc older.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
-  >   throw 2>&1 | egrep '^\*\*'
-  ** Unknown exception encountered with possibly-broken third-party extension throw
-  ** which supports versions 2.1 of Mercurial.
-  ** Please disable throw and try your action again.
-  ** If that fixes the bug please report it to http://example.com/bts
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (version 1.9.3)
-  ** Extensions loaded: throw, older
-
-Ability to point to a different point
-  $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
-  >   --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
-  ** unknown exception encountered, please report by visiting
-  ** Your Local Goat Lenders
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (*) (glob)
-  ** Extensions loaded: throw, older
-
-Declare the version as supporting this hg version, show regular bts link:
-  $ hgver=`hg debuginstall -T '{hgver}'`
-  $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
-  $ if [ -z "$hgver" ]; then
-  >   echo "unable to fetch a mercurial version. Make sure __version__ is correct";
-  > fi
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
-  ** unknown exception encountered, please report by visiting
-  ** https://mercurial-scm.org/wiki/BugTracker
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (*) (glob)
-  ** Extensions loaded: throw
-
-Patch version is ignored during compatibility check
-  $ echo "testedwith = b'3.2'" >> throw.py
-  $ echo "util.version = lambda:b'3.2.2'" >> throw.py
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
-  ** unknown exception encountered, please report by visiting
-  ** https://mercurial-scm.org/wiki/BugTracker
-  ** Python * (glob)
-  ** Mercurial Distributed SCM (*) (glob)
-  ** Extensions loaded: throw
-
-Test version number support in 'hg version':
-  $ echo '__version__ = (1, 2, 3)' >> throw.py
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg version -v
-  Mercurial Distributed SCM (version *) (glob)
-  (see https://mercurial-scm.org for more information)
-  
-  Copyright (C) 2005-* Matt Mackall and others (glob)
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  
-  Enabled extensions:
-  
-
-  $ hg version -v --config extensions.throw=throw.py
-  Mercurial Distributed SCM (version *) (glob)
-  (see https://mercurial-scm.org for more information)
-  
-  Copyright (C) 2005-* Matt Mackall and others (glob)
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  
-  Enabled extensions:
-  
-    throw  external  1.2.3
-  $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
-  $ rm -f throw.pyc throw.pyo
-  $ rm -Rf __pycache__
-  $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
-  Mercurial Distributed SCM (version *) (glob)
-  (see https://mercurial-scm.org for more information)
-  
-  Copyright (C) 2005-* Matt Mackall and others (glob)
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  
-  Enabled extensions:
-  
-    throw  external  1.twentythree
-    strip  internal  
-
-  $ hg version -q --config extensions.throw=throw.py
-  Mercurial Distributed SCM (version *) (glob)
-
-Test template output:
-
-  $ hg version --config extensions.strip= -T'{extensions}'
-  strip
-
-Test JSON output of version:
-
-  $ hg version -Tjson
-  [
-   {
-    "extensions": [],
-    "ver": "*" (glob)
-   }
-  ]
-
-  $ hg version --config extensions.throw=throw.py -Tjson
-  [
-   {
-    "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
-    "ver": "3.2.2"
-   }
-  ]
-
-  $ hg version --config extensions.strip= -Tjson
-  [
-   {
-    "extensions": [{"bundled": true, "name": "strip", "ver": null}],
-    "ver": "*" (glob)
-   }
-  ]
-
-Test template output of version:
-
-  $ hg version --config extensions.throw=throw.py --config extensions.strip= \
-  > -T'{extensions % "{name}  {pad(ver, 16)}  ({if(bundled, "internal", "external")})\n"}'
-  throw  1.twentythree     (external)
-  strip                    (internal)
-
-Refuse to load extensions with minimum version requirements
-
-  $ cat > minversion1.py << EOF
-  > from mercurial import util
-  > util.version = lambda: b'3.5.2'
-  > minimumhgversion = b'3.6'
-  > EOF
-  $ hg --config extensions.minversion=minversion1.py version
-  (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
-  Mercurial Distributed SCM (version 3.5.2)
-  (see https://mercurial-scm.org for more information)
-  
-  Copyright (C) 2005-* Matt Mackall and others (glob)
-  This is free software; see the source for copying conditions. There is NO
-  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-  $ cat > minversion2.py << EOF
-  > from mercurial import util
-  > util.version = lambda: b'3.6'
-  > minimumhgversion = b'3.7'
-  > EOF
-  $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
-  (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
-
-Can load version that is only off by point release
-
-  $ cat > minversion2.py << EOF
-  > from mercurial import util
-  > util.version = lambda: b'3.6.1'
-  > minimumhgversion = b'3.6'
-  > EOF
-  $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
-  [1]
-
-Can load minimum version identical to current
-
-  $ cat > minversion3.py << EOF
-  > from mercurial import util
-  > util.version = lambda: b'3.5'
-  > minimumhgversion = b'3.5'
-  > EOF
-  $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
-  [1]
-
-Restore HGRCPATH
-
-  $ HGRCPATH=$ORGHGRCPATH
-  $ export HGRCPATH
-
-Commands handling multiple repositories at a time should invoke only
-"reposetup()" of extensions enabling in the target repository.
-
-  $ mkdir reposetup-test
-  $ cd reposetup-test
-
-  $ cat > $TESTTMP/reposetuptest.py <<EOF
-  > from mercurial import extensions
-  > def reposetup(ui, repo):
-  >     ui.write(b'reposetup() for %s\n' % (repo.root))
-  >     ui.flush()
-  > EOF
-  $ hg init src
-  $ echo a > src/a
-  $ hg -R src commit -Am '#0 at src/a'
-  adding a
-  $ echo '[extensions]' >> src/.hg/hgrc
-  $ echo '# enable extension locally' >> src/.hg/hgrc
-  $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
-  $ hg -R src status
-  reposetup() for $TESTTMP/reposetup-test/src
-  reposetup() for $TESTTMP/reposetup-test/src (chg !)
-
-#if no-extraextensions
-  $ hg --cwd src debugextensions
-  reposetup() for $TESTTMP/reposetup-test/src
-  dodo (untested!)
-  dudu (untested!)
-  mq
-  reposetuptest (untested!)
-  strip
-#endif
-
-  $ hg clone -U src clone-dst1
-  reposetup() for $TESTTMP/reposetup-test/src
-  $ hg init push-dst1
-  $ hg -q -R src push push-dst1
-  reposetup() for $TESTTMP/reposetup-test/src
-  $ hg init pull-src1
-  $ hg -q -R pull-src1 pull src
-  reposetup() for $TESTTMP/reposetup-test/src
-
-  $ cat <<EOF >> $HGRCPATH
-  > [extensions]
-  > # disable extension globally and explicitly
-  > reposetuptest = !
-  > EOF
-  $ hg clone -U src clone-dst2
-  reposetup() for $TESTTMP/reposetup-test/src
-  $ hg init push-dst2
-  $ hg -q -R src push push-dst2
-  reposetup() for $TESTTMP/reposetup-test/src
-  $ hg init pull-src2
-  $ hg -q -R pull-src2 pull src
-  reposetup() for $TESTTMP/reposetup-test/src
-
-  $ cat <<EOF >> $HGRCPATH
-  > [extensions]
-  > # enable extension globally
-  > reposetuptest = $TESTTMP/reposetuptest.py
-  > EOF
-  $ hg clone -U src clone-dst3
-  reposetup() for $TESTTMP/reposetup-test/src
-  reposetup() for $TESTTMP/reposetup-test/clone-dst3
-  $ hg init push-dst3
-  reposetup() for $TESTTMP/reposetup-test/push-dst3
-  $ hg -q -R src push push-dst3
-  reposetup() for $TESTTMP/reposetup-test/src
-  reposetup() for $TESTTMP/reposetup-test/push-dst3
-  $ hg init pull-src3
-  reposetup() for $TESTTMP/reposetup-test/pull-src3
-  $ hg -q -R pull-src3 pull src
-  reposetup() for $TESTTMP/reposetup-test/pull-src3
-  reposetup() for $TESTTMP/reposetup-test/src
-
-  $ echo '[extensions]' >> src/.hg/hgrc
-  $ echo '# disable extension locally' >> src/.hg/hgrc
-  $ echo 'reposetuptest = !' >> src/.hg/hgrc
-  $ hg clone -U src clone-dst4
-  reposetup() for $TESTTMP/reposetup-test/clone-dst4
-  $ hg init push-dst4
-  reposetup() for $TESTTMP/reposetup-test/push-dst4
-  $ hg -q -R src push push-dst4
-  reposetup() for $TESTTMP/reposetup-test/push-dst4
-  $ hg init pull-src4
-  reposetup() for $TESTTMP/reposetup-test/pull-src4
-  $ hg -q -R pull-src4 pull src
-  reposetup() for $TESTTMP/reposetup-test/pull-src4
-
-disabling in command line overlays with all configuration
-  $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
-  $ hg --config extensions.reposetuptest=! init push-dst5
-  $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
-  $ hg --config extensions.reposetuptest=! init pull-src5
-  $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
-
-  $ cat <<EOF >> $HGRCPATH
-  > [extensions]
-  > # disable extension globally and explicitly
-  > reposetuptest = !
-  > EOF
-  $ hg init parent
-  $ hg init parent/sub1
-  $ echo 1 > parent/sub1/1
-  $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
-  adding 1
-  $ hg init parent/sub2
-  $ hg init parent/sub2/sub21
-  $ echo 21 > parent/sub2/sub21/21
-  $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
-  adding 21
-  $ cat > parent/sub2/.hgsub <<EOF
-  > sub21 = sub21
-  > EOF
-  $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
-  adding .hgsub
-  $ hg init parent/sub3
-  $ echo 3 > parent/sub3/3
-  $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
-  adding 3
-  $ cat > parent/.hgsub <<EOF
-  > sub1 = sub1
-  > sub2 = sub2
-  > sub3 = sub3
-  > EOF
-  $ hg -R parent commit -Am '#0 at parent'
-  adding .hgsub
-  $ echo '[extensions]' >> parent/.hg/hgrc
-  $ echo '# enable extension locally' >> parent/.hg/hgrc
-  $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
-  $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
-  $ hg -R parent status -S -A
-  reposetup() for $TESTTMP/reposetup-test/parent
-  reposetup() for $TESTTMP/reposetup-test/parent/sub2
-  C .hgsub
-  C .hgsubstate
-  C sub1/1
-  C sub2/.hgsub
-  C sub2/.hgsubstate
-  C sub2/sub21/21
-  C sub3/3
-
-  $ cd ..
-
-Prohibit registration of commands that don't use @command (issue5137)
-
-  $ hg init deprecated
-  $ cd deprecated
-
-  $ cat <<EOF > deprecatedcmd.py
-  > def deprecatedcmd(repo, ui):
-  >     pass
-  > cmdtable = {
-  >     b'deprecatedcmd': (deprecatedcmd, [], b''),
-  > }
-  > EOF
-  $ cat <<EOF > .hg/hgrc
-  > [extensions]
-  > deprecatedcmd = `pwd`/deprecatedcmd.py
-  > mq = !
-  > hgext.mq = !
-  > hgext/mq = !
-  > EOF
-
-  $ hg deprecatedcmd > /dev/null
-  *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
-  *** (use @command decorator to register 'deprecatedcmd')
-  hg: unknown command 'deprecatedcmd'
-  (use 'hg help' for a list of commands)
-  [255]
-
- the extension shouldn't be loaded at all so the mq works:
-
-  $ hg qseries --config extensions.mq= > /dev/null
-  *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
-  *** (use @command decorator to register 'deprecatedcmd')
-
-  $ cd ..
-
-Test synopsis and docstring extending
-
-  $ hg init exthelp
-  $ cat > exthelp.py <<EOF
-  > from mercurial import commands, extensions
-  > def exbookmarks(orig, *args, **opts):
-  >     return orig(*args, **opts)
-  > def uisetup(ui):
-  >     synopsis = b' GREPME [--foo] [-x]'
-  >     docstring = '''
-  >     GREPME make sure that this is in the help!
-  >     '''
-  >     extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
-  >                            synopsis, docstring)
-  > EOF
-  $ abspath=`pwd`/exthelp.py
-  $ echo '[extensions]' >> $HGRCPATH
-  $ echo "exthelp = $abspath" >> $HGRCPATH
-  $ cd exthelp
-  $ hg help bookmarks | grep GREPME
-  hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
-      GREPME make sure that this is in the help!
-  $ cd ..
-
-Show deprecation warning for the use of cmdutil.command
-
-  $ cat > nonregistrar.py <<EOF
-  > from mercurial import cmdutil
-  > cmdtable = {}
-  > command = cmdutil.command(cmdtable)
-  > @command(b'foo', [], norepo=True)
-  > def foo(ui):
-  >     pass
-  > EOF
-
-Prohibit the use of unicode strings as the default value of options
-
-  $ hg init $TESTTMP/opt-unicode-default
-
-  $ cat > $TESTTMP/test_unicode_default_value.py << EOF
-  > from mercurial import registrar
-  > cmdtable = {}
-  > command = registrar.command(cmdtable)
-  > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
-  > def ext(*args, **opts):
-  >     print(opts[b'opt'])
-  > EOF
-  $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
-  > [extensions]
-  > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
-  > EOF
-  $ hg -R $TESTTMP/opt-unicode-default dummy
-  *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode u'value' found in cmdtable.dummy
-  *** (use b'' to make it byte string)
-  hg: unknown command 'dummy'
-  (did you mean summary?)
-  [255]
+  $ echo 'foobar = !' >> $HGRCPATH
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -164,8 +164,8 @@
     obj = _peerlookup(path).instance(ui, path, create, intents=intents)
     ui = getattr(obj, "ui", ui)
     if ui.configbool('devel', 'debug.extensions'):
-        log = functools.partial(
-            ui.debug, 'debug.extensions: ', label='debug.extensions')
+        log = lambda msg, *values: ui.debug('debug.extensions: ',
+            msg % values, label='debug.extensions')
     else:
         log = lambda *a, **kw: None
     for f in presetupfuncs or []:
@@ -175,7 +175,9 @@
         log('  - running reposetup for %s\n' % (name,))
         hook = getattr(module, 'reposetup', None)
         if hook:
-            hook(ui, obj)
+            with util.timedcm('reposetup %r', name) as stats:
+                hook(ui, obj)
+            log('  > reposetup for %r took %s\n', name, stats)
     if not obj.local():
         for f in wirepeersetupfuncs:
             f(ui, obj)



To: lothiraldan, #hg-reviewers
Cc: mjpieters, mercurial-devel


More information about the Mercurial-devel mailing list