[PATCH 1 of 2] extensions: permit extension interdependency through load-time hooking
Brodie Rao
dackze at gmail.com
Fri Aug 7 17:05:15 UTC 2009
# HG changeset patch
# User Brodie Rao <me+hg at dackz.net>
# Date 1249663609 14400
# Node ID 75717f90c7e90c9e3a061a223d5ca9c776e7a4dd
# Parent 9a69ab6d7cf765c1a3ed43ae8b0e5d770c6eacf6
extensions: permit extension interdependency through load-time hooking
To let an extension safely use or modify another extension, a new API is
added: extensions.hook(shortname, func). If the extension identified by
shortname is already loaded, func is executed immediately; otherwise, func
is executed immediately after the extension is loaded, before its
uisetup() is called.
For example, the color extension wraps various mq commands. In the past it
has used two different methods of finding mq to wrap it:
1. from hgext import mq
This bypasses the extension machinery, and can potentially load a different
mq than what the user specified in hgrc.
2. extensions.find('mq')
This fails if mq is not yet loaded. Whether it's loaded yet depends on the
order of [extensions] in hgrc.
Using extensions.hook(), the hook function is only executed if the target
extension is loaded, and it doesn't cause the wrong mq to be loaded.
diff --git a/hgext/color.py b/hgext/color.py
--- a/hgext/color.py
+++ b/hgext/color.py
@@ -219,12 +219,8 @@ _diff_effects = {'diffline': ['bold'],
'changed': ['white'],
'trailingwhitespace': ['bold', 'red_background']}
-_ui = None
-
def uisetup(ui):
'''Initialize the extension.'''
- global _ui
- _ui = ui
_setupcmd(ui, 'diff', commands.table, colordiff, _diff_effects)
_setupcmd(ui, 'incoming', commands.table, None, _diff_effects)
_setupcmd(ui, 'log', commands.table, None, _diff_effects)
@@ -232,20 +228,10 @@ def uisetup(ui):
_setupcmd(ui, 'tip', commands.table, None, _diff_effects)
_setupcmd(ui, 'status', commands.table, colorstatus, _status_effects)
-def extsetup():
- try:
- mq = extensions.find('mq')
- try:
- # If we are loaded after mq, we must wrap commands.table
- _setupcmd(_ui, 'qdiff', commands.table, colordiff, _diff_effects)
- _setupcmd(_ui, 'qseries', commands.table, colorqseries, _patch_effects)
- except error.UnknownCommand:
- # Otherwise we wrap mq.cmdtable
- _setupcmd(_ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
- _setupcmd(_ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
- except KeyError:
- # The mq extension is not enabled
- pass
+ def setupmq(mq):
+ _setupcmd(ui, 'qdiff', mq.cmdtable, colordiff, _diff_effects)
+ _setupcmd(ui, 'qseries', mq.cmdtable, colorqseries, _patch_effects)
+ extensions.hook('mq', setupmq)
def _setupcmd(ui, cmd, table, func, effectsmap):
'''patch in command to command table and load effect map'''
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -10,6 +10,7 @@ import util, cmdutil, help
from i18n import _, gettext
_extensions = {}
+_hooks = {}
_order = []
def extensions():
@@ -66,6 +67,18 @@ def load(ui, name, path):
_extensions[shortname] = mod
_order.append(shortname)
+ for func in _hooks.pop(shortname, []):
+ try:
+ func(mod)
+ except KeyboardInterrupt:
+ raise
+ except Exception, inst:
+ errname = func.__module__.rsplit('.', 1)[1]
+ ui.warn(_('*** extension %s failed to hook extension %s: %s\n')
+ % (errname, name, inst))
+ if ui.traceback():
+ raise
+
uisetup = getattr(mod, 'uisetup', None)
if uisetup:
uisetup(ui)
@@ -90,6 +103,14 @@ def loadall(ui):
if ui.traceback():
return 1
+def hook(shortname, func):
+ if shortname in _extensions:
+ func(_extensions[shortname])
+ elif shortname in _hooks:
+ _hooks[shortname].append(func)
+ else:
+ _hooks[shortname] = [func]
+
def wrapcommand(table, command, wrapper):
aliases, entry = cmdutil.findcmd(command, table)
for alias, e in table.iteritems():
diff --git a/tests/test-extension b/tests/test-extension
--- a/tests/test-extension
+++ b/tests/test-extension
@@ -116,3 +116,30 @@ echo "hgext/mq=" >> $HGRCPATH
echo % show extensions
hg debugextensions
+echo 'mq = !' >> $HGRCPATH
+echo 'hgext.mq = !' >> $HGRCPATH
+echo 'hgext/mq = !' >> $HGRCPATH
+
+echo % hook extensions
+cat > debughookextension.py <<EOF
+'''hook another extension
+'''
+from mercurial import extensions
+
+def wrap(ext):
+ def wrapper(orig, ui, *args, **kw):
+ ui.write('wrapped\n')
+ extensions.wrapcommand(ext.cmdtable, 'debugextensions', wrapper)
+
+def uisetup(ui):
+ extensions.hook('debugissue811', wrap)
+EOF
+debughookpath=`pwd`/debughookextension.py
+echo "debugissue811 = $debugpath" >> $HGRCPATH
+echo "debughookextension = $debughookpath" >> $HGRCPATH
+hg debugextensions
+echo 'debugissue811 = !' >> $HGRCPATH
+echo 'debughookextension = !' >> $HGRCPATH
+echo "debughookextension = $debughookpath" >> $HGRCPATH
+echo "debugissue811 = $debugpath" >> $HGRCPATH
+hg debugextensions
diff --git a/tests/test-extension.out b/tests/test-extension.out
--- a/tests/test-extension.out
+++ b/tests/test-extension.out
@@ -85,3 +85,6 @@ global options:
% show extensions
debugissue811
mq
+% hook extensions
+wrapped
+wrapped
More information about the Mercurial-devel
mailing list