extensions: support callbacks after another extension loads
authorGregory Szorc <gregory.szorc@gmail.com>
Fri, 06 Feb 2015 12:07:32 -0800
changeset 24065 d8837ad682dd
parent 24064 c260887cdbcd
child 24066 afdf5f6ab37a
extensions: support callbacks after another extension loads An upcoming patch will introduce a dependency between the color and pager extensions. To prepare for this, we teach extensions how to register callbacks that can execute when another extension loads. This patch is based on code provided by Matt Mackall. But significant parts have changed (such as the ability to register multiple callbacks and the change in behavior to always call a callback). I believe that always firing the callback is a good practice. I think the common use for this feature will be for extensions to say "run this one-time setup code, after this other extension if possible." Always running the callback will facilitate this.
mercurial/extensions.py
--- a/mercurial/extensions.py	Fri Feb 06 21:53:39 2015 +0900
+++ b/mercurial/extensions.py	Fri Feb 06 12:07:32 2015 -0800
@@ -10,6 +10,7 @@
 from i18n import _, gettext
 
 _extensions = {}
+_aftercallbacks = {}
 _order = []
 _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg', 'inotify']
 
@@ -87,6 +88,8 @@
             mod = importh(name)
     _extensions[shortname] = mod
     _order.append(shortname)
+    for fn in _aftercallbacks.get(shortname, []):
+        fn(loaded=True)
     return mod
 
 def loadall(ui):
@@ -123,6 +126,32 @@
                     raise
                 extsetup() # old extsetup with no ui argument
 
+    # Call aftercallbacks that were never met.
+    for shortname in _aftercallbacks:
+        if shortname in _extensions:
+            continue
+
+        for fn in _aftercallbacks[shortname]:
+            fn(loaded=False)
+
+def afterloaded(extension, callback):
+    '''Run the specified function after a named extension is loaded.
+
+    If the named extension is already loaded, the callback will be called
+    immediately.
+
+    If the named extension never loads, the callback will be called after
+    all extensions have been loaded.
+
+    The callback receives the named argument ``loaded``, which is a boolean
+    indicating whether the dependent extension actually loaded.
+    '''
+
+    if extension in _extensions:
+        callback(loaded=False)
+    else:
+        _aftercallbacks.setdefault(extension, []).append(callback)
+
 def wrapcommand(table, command, wrapper):
     '''Wrap the command named `command' in table