Fun with loading extensions
Greg Ward
greg at gerg.ca
Tue Jun 15 12:07:22 CDT 2010
On Tue, Jun 15, 2010 at 10:52 AM, Dirkjan Ochtman <dirkjan at ochtman.nl> wrote:
> Augie and I discussed this a little bit in IRC yesterday, and we
> seemed to agree that using wrapfunction() for (repo) methods is weird,
> and everyone should use subclassing and call super() instead.
> wrapfunction() is really intended for *functions*, not methods.
OK, but that only works if you want to wrap^H^H^H^Hoverride a method
of an object available in reposetup(): ui or repo. In practice that's
fine, because people really only seem to use the "dynamic subclass"
trick on the repo object.
But if you want to wrap a method of, say, changectx, then you have no
choice but to use wrapfunction(). (I have one extension that does
this to insert info into the commit message: it's a bit tricky, but it
works.)
Here's a doc patch:
# HG changeset patch
# User Greg Ward <greg-hg at gerg.ca>
# Date 1276621462 14400
# Node ID e2bf80d7124a67507d988c84e5ff2b61673bdceb
# Parent ad0a334eef163df857c36fdca2bd4daadba9df99
extensions: recommend against using wrapfunction() for repo methods.
Instead, all extensions should use the "dynamic subclass" trick:
subclass repo.__class__ and then replace repo.__class__ with your new
subclass. This avoids conflicts that happen when one extension uses
wrapfunction and another uses subclassing to extend the same method of
localrepository.
diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -124,6 +124,30 @@
return entry
def wrapfunction(container, funcname, wrapper):
+ '''Wrap the function named funcname in container by replacing it with
+ your wrapper. container is typically a module, class, or instance.
+
+ wrapper will be called like
+ wrapper(orig, *args, **kwargs)
+ where orig is the original (wrapped) function, and *args, **kwargs are the
+ arguments passed to it.
+
+ Wrapping methods of the repository object is not recommended since it
+ conflicts with extensions that extend the repository by subclassing.
+ All extensions that need to extend methods of localrepository should
+ use this subclassing trick: namely, reposetup() should look like
+ def reposetup(ui, repo):
+ class myrepo(repo.__class__):
+ def whatever(self, *args, **kwargs):
+ [...extension stuff...]
+ super(myrepo, self).whatever(*args, **kwargs)
+ [...extension stuff...]
+
+ repo.__class__ = myrepo
+
+ In general, combining wrapfunction() with subclassing does not work. Since
+ you cannot control what other extensions are loaded by your end users,
+ you should play nicely with others by using the subclass trick.'''
def wrap(*args, **kwargs):
return wrapper(origfn, *args, **kwargs)
If people like this, there's a wiki page or two that needs updating too.
Greg
More information about the Mercurial-devel
mailing list