[PATCH 2 of 4] extensions: introduce a class interposition function

Bryan O'Sullivan bos at serpentine.com
Mon Jun 25 16:58:32 CDT 2012


# HG changeset patch
# User Bryan O'Sullivan <bryano at fb.com>
# Date 1340657771 25200
# Node ID e5a5191a08b8077e4d05e7bf32552cf382db07dd
# Parent  5c2fe39e4efcc5c70797afe149054a38855e7516
extensions: introduce a class interposition function

This allows an existing class to be augmented in a transparent way,
without its subclasses or callers needing to participate.

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -188,6 +188,34 @@ def wrapfunction(container, funcname, wr
     setattr(container, funcname, wrap)
     return origfn
 
+def replaceclass(container, classname):
+    '''Replace a class with another in a module, and interpose it into
+    the hierarchies of all loaded subclasses. This function is
+    intended for use as a decorator.
+
+      import mymodule
+      @replaceclass(mymodule, 'myclass')
+      class mysubclass(mymodule.myclass):
+          def foo(self):
+              f = super(mysubclass, self).foo()
+              return f + ' bar'
+
+    Existing instances of the class being replaced will not have their
+    __class__ modified, so call this function before creating any
+    objects of the target type.
+    '''
+    def wrap(cls):
+        oldcls = getattr(container, classname)
+        oldbases = (oldcls,)
+        newbases = (cls,)
+        for subcls in oldcls.__subclasses__():
+            if subcls is not cls:
+                assert subcls.__bases__ == oldbases
+                subcls.__bases__ = newbases
+        setattr(container, classname, cls)
+        return cls
+    return wrap
+
 def _disabledpaths(strip_init=False):
     '''find paths of disabled extensions. returns a dict of {name: path}
     removes /__init__.py from packages if strip_init is True'''


More information about the Mercurial-devel mailing list