extensions: load and configure extensions in well-defined phases
authorMartin Geisler <mg@lazybytes.net>
Sat, 29 Aug 2009 00:29:16 +0200
changeset 9410 1c83938b6a8e
parent 9409 57157a224037
child 9411 8e6019b16a7d
extensions: load and configure extensions in well-defined phases Extensions are now loaded with a call-graph like this: dispatch._dispatch extensions.loadall extensions.load # add foo module to extensions._extensions extensions.load # add bar module to extensions._extensions foo.uisetup(ui) bar.uisetup(ui) foo.extsetup() bar.extsetup() commands.table.update(foo.cmdtable) commands.table.update(bar.cmdtable) hg.repository foo.reposetup(ui, repo) bar.reposetup(ui, repo) The uisetup calls could easily be moved out to dispatch._dispatch, but have been kept in extensions.loadall since at least TortoiseHg calls extensions.loadall and expects it to call uisetup. The extensions.load function called uisetup. It now has an unused ui argument which has been kept for backwards compatibility.
mercurial/dispatch.py
mercurial/extensions.py
tests/test-extension
tests/test-extension.out
--- a/mercurial/dispatch.py	Fri Aug 28 22:14:04 2009 +0200
+++ b/mercurial/dispatch.py	Sat Aug 29 00:29:16 2009 +0200
@@ -349,19 +349,20 @@
         lui = ui.copy()
         lui.readconfig(os.path.join(path, ".hg", "hgrc"))
 
+    # Configure extensions in phases: uisetup, extsetup, cmdtable, and
+    # reposetup. Programs like TortoiseHg will call _dispatch several
+    # times so we keep track of configured extensions in _loaded.
     extensions.loadall(lui)
-    for name, module in extensions.extensions():
-        if name in _loaded:
-            continue
+    exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
 
-        # setup extensions
-        # TODO this should be generalized to scheme, where extensions can
-        #      redepend on other extensions.  then we should toposort them, and
-        #      do initialization in correct order
+    # (uisetup is handled in extensions.loadall)
+
+    for name, module in exts:
         extsetup = getattr(module, 'extsetup', None)
         if extsetup:
             extsetup()
 
+    for name, module in exts:
         cmdtable = getattr(module, 'cmdtable', {})
         overrides = [cmd for cmd in cmdtable if cmd in commands.table]
         if overrides:
@@ -370,6 +371,8 @@
         commands.table.update(cmdtable)
         _loaded.add(name)
 
+    # (reposetup is handled in hg.repository)
+
     addaliases(lui, commands.table)
 
     # check for fallback encoding
--- a/mercurial/extensions.py	Fri Aug 28 22:14:04 2009 +0200
+++ b/mercurial/extensions.py	Sat Aug 29 00:29:16 2009 +0200
@@ -40,6 +40,7 @@
         return imp.load_source(module_name, path)
 
 def load(ui, name, path):
+    # unused ui argument kept for backwards compatibility
     if name.startswith('hgext.') or name.startswith('hgext/'):
         shortname = name[6:]
     else:
@@ -66,12 +67,9 @@
     _extensions[shortname] = mod
     _order.append(shortname)
 
-    uisetup = getattr(mod, 'uisetup', None)
-    if uisetup:
-        uisetup(ui)
-
 def loadall(ui):
     result = ui.configitems("extensions")
+    newindex = len(_order)
     for (name, path) in result:
         if path:
             if path[0] == '!':
@@ -90,6 +88,11 @@
             if ui.traceback():
                 return 1
 
+    for name in _order[newindex:]:
+        uisetup = getattr(_extensions[name], 'uisetup', None)
+        if uisetup:
+            uisetup(ui)
+
 def wrapcommand(table, command, wrapper):
     aliases, entry = cmdutil.findcmd(command, table)
     for alias, e in table.iteritems():
--- a/tests/test-extension	Fri Aug 28 22:14:04 2009 +0200
+++ b/tests/test-extension	Sat Aug 29 00:29:16 2009 +0200
@@ -55,6 +55,29 @@
 hg 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
+EOF
+
+cp foo.py bar.py
+echo 'foo = foo.py' >> $HGRCPATH
+echo 'bar = bar.py' >> $HGRCPATH
+
+# command with no output, we just want to see the extensions loaded
+hg paths
+
+echo 'foo = !' >> $HGRCPATH
+echo 'bar = !' >> $HGRCPATH
+
 cd ..
 cat > empty.py <<EOF
 '''empty cmdtable
--- a/tests/test-extension.out	Fri Aug 28 22:14:04 2009 +0200
+++ b/tests/test-extension.out	Sat Aug 29 00:29:16 2009 +0200
@@ -16,6 +16,14 @@
 reposetup called for a
 ui == repo.ui
 Foo
+1) foo imported
+1) bar imported
+2) foo uisetup
+2) bar uisetup
+3) foo extsetup
+3) bar extsetup
+4) foo reposetup
+4) bar reposetup
 empty extension - empty cmdtable
 
 no commands defined