Patchwork [1,of,4,V2] extensions: support callbacks after another extension loads

mail settings
Submitter Gregory Szorc
Date Feb. 6, 2015, 8:11 p.m.
Message ID <c6e2f3414f33e46ecbc1.1423253461@gps-mbp.local>
Download mbox | patch
Permalink /patch/7721/
State Accepted
Commit d8837ad682dd34be20a1008e76e5e62e2584e3b0
Headers show


Gregory Szorc - Feb. 6, 2015, 8:11 p.m.
# HG changeset patch
# User Gregory Szorc <>
# Date 1423253252 28800
#      Fri Feb 06 12:07:32 2015 -0800
# Node ID c6e2f3414f33e46ecbc1788e38be9c79a1ce6e50
# Parent  a9b61dbdb827165a9fd9d44dd42b892dbd9fa07c
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.


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -9,8 +9,9 @@  import imp, os
 import util, cmdutil, error
 from i18n import _, gettext
 _extensions = {}
+_aftercallbacks = {}
 _order = []
 _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg', 'inotify']
 def extensions(ui=None):
@@ -86,8 +87,10 @@  def load(ui, name, path):
                      % (name, err, name))
             mod = importh(name)
     _extensions[shortname] = mod
+    for fn in _aftercallbacks.get(shortname, []):
+        fn(loaded=True)
     return mod
 def loadall(ui):
     result = ui.configitems("extensions")
@@ -122,8 +125,34 @@  def loadall(ui):
                 if extsetup.func_code.co_argcount != 0:
                 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
     Replace command in the command table with wrapper. The wrapped command will