diff mercurial/localrepo.py @ 2155:ff255b41b4aa

support hooks written in python. to write hook in python, create module with hook function inside. make sure mercurial can import module (put it in $PYTHONPATH or load it as extension). hook function should look like this: def myhook(ui, repo, hooktype, **kwargs): if hook_passes: return True elif hook_explicitly_fails: return False elif some_other_failure: import util raise util.Abort('helpful failure message') else: return # implicit return of None makes hook fail! then in .hgrc, add hook with "python:" prefix: [hooks] commit = python:mymodule.myhook
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Fri, 28 Apr 2006 15:50:22 -0700
parents f15c6394d90d
children 628bf85f07ee
line wrap: on
line diff
--- a/mercurial/localrepo.py	Fri Apr 28 14:50:23 2006 -0700
+++ b/mercurial/localrepo.py	Fri Apr 28 15:50:22 2006 -0700
@@ -11,7 +11,8 @@
 from i18n import gettext as _
 from demandload import *
 demandload(globals(), "appendfile changegroup")
-demandload(globals(), "re lock transaction tempfile stat mdiff errno ui revlog")
+demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
+demandload(globals(), "revlog sys traceback")
 
 class localrepository(object):
     def __del__(self):
@@ -71,7 +72,59 @@
             os.mkdir(self.join("data"))
 
         self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
+
     def hook(self, name, throw=False, **args):
+        def callhook(hname, funcname):
+            '''call python hook. hook is callable object, looked up as
+            name in python module. if callable returns "true", hook
+            passes, else fails. if hook raises exception, treated as
+            hook failure. exception propagates if throw is "true".'''
+
+            self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
+            d = funcname.rfind('.')
+            if d == -1:
+                raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
+                                 % (hname, funcname))
+            modname = funcname[:d]
+            try:
+                obj = __import__(modname)
+            except ImportError:
+                raise util.Abort(_('%s hook is invalid '
+                                   '(import of "%s" failed)') %
+                                 (hname, modname))
+            try:
+                for p in funcname.split('.')[1:]:
+                    obj = getattr(obj, p)
+            except AttributeError, err:
+                raise util.Abort(_('%s hook is invalid '
+                                   '("%s" is not defined)') %
+                                 (hname, funcname))
+            if not callable(obj):
+                raise util.Abort(_('%s hook is invalid '
+                                   '("%s" is not callable)') %
+                                 (hname, funcname))
+            try:
+                r = obj(ui=ui, repo=repo, hooktype=name, **args)
+            except (KeyboardInterrupt, util.SignalInterrupt):
+                raise
+            except Exception, exc:
+                if isinstance(exc, util.Abort):
+                    self.ui.warn(_('error: %s hook failed: %s\n') %
+                                 (hname, exc.args[0] % exc.args[1:]))
+                else:
+                    self.ui.warn(_('error: %s hook raised an exception: '
+                                   '%s\n') % (hname, exc))
+                if throw:
+                    raise
+                if "--traceback" in sys.argv[1:]:
+                    traceback.print_exc()
+                return False
+            if not r:
+                if throw:
+                    raise util.Abort(_('%s hook failed') % hname)
+                self.ui.warn(_('error: %s hook failed\n') % hname)
+            return r
+
         def runhook(name, cmd):
             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
             env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
@@ -90,7 +143,10 @@
                  if hname.split(".", 1)[0] == name and cmd]
         hooks.sort()
         for hname, cmd in hooks:
-            r = runhook(hname, cmd) and r
+            if cmd.startswith('python:'):
+                r = callhook(hname, cmd[7:].strip()) and r
+            else:
+                r = runhook(hname, cmd) and r
         return r
 
     def tags(self):