mercurial/hook.py
changeset 4623 fff50306e6dd
equal deleted inserted replaced
4622:6fc26982f203 4623:fff50306e6dd
       
     1 # hook.py - hook support for mercurial
       
     2 #
       
     3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms
       
     6 # of the GNU General Public License, incorporated herein by reference.
       
     7 
       
     8 from i18n import _
       
     9 import util
       
    10 
       
    11 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
       
    12     '''call python hook. hook is callable object, looked up as
       
    13     name in python module. if callable returns "true", hook
       
    14     fails, else passes. if hook raises exception, treated as
       
    15     hook failure. exception propagates if throw is "true".
       
    16 
       
    17     reason for "true" meaning "hook failed" is so that
       
    18     unmodified commands (e.g. mercurial.commands.update) can
       
    19     be run as hooks without wrappers to convert return values.'''
       
    20 
       
    21     ui.note(_("calling hook %s: %s\n") % (hname, funcname))
       
    22     obj = funcname
       
    23     if not callable(obj):
       
    24         d = funcname.rfind('.')
       
    25         if d == -1:
       
    26             raise util.Abort(_('%s hook is invalid ("%s" not in '
       
    27                                'a module)') % (hname, funcname))
       
    28         modname = funcname[:d]
       
    29         try:
       
    30             obj = __import__(modname)
       
    31         except ImportError:
       
    32             try:
       
    33                 # extensions are loaded with hgext_ prefix
       
    34                 obj = __import__("hgext_%s" % modname)
       
    35             except ImportError:
       
    36                 raise util.Abort(_('%s hook is invalid '
       
    37                                    '(import of "%s" failed)') %
       
    38                                  (hname, modname))
       
    39         try:
       
    40             for p in funcname.split('.')[1:]:
       
    41                 obj = getattr(obj, p)
       
    42         except AttributeError, err:
       
    43             raise util.Abort(_('%s hook is invalid '
       
    44                                '("%s" is not defined)') %
       
    45                              (hname, funcname))
       
    46         if not callable(obj):
       
    47             raise util.Abort(_('%s hook is invalid '
       
    48                                '("%s" is not callable)') %
       
    49                              (hname, funcname))
       
    50     try:
       
    51         r = obj(ui=ui, repo=repo, hooktype=name, **args)
       
    52     except (KeyboardInterrupt, util.SignalInterrupt):
       
    53         raise
       
    54     except Exception, exc:
       
    55         if isinstance(exc, util.Abort):
       
    56             ui.warn(_('error: %s hook failed: %s\n') %
       
    57                          (hname, exc.args[0]))
       
    58         else:
       
    59             ui.warn(_('error: %s hook raised an exception: '
       
    60                            '%s\n') % (hname, exc))
       
    61         if throw:
       
    62             raise
       
    63         ui.print_exc()
       
    64         return True
       
    65     if r:
       
    66         if throw:
       
    67             raise util.Abort(_('%s hook failed') % hname)
       
    68         ui.warn(_('warning: %s hook failed\n') % hname)
       
    69     return r
       
    70 
       
    71 def _exthook(ui, repo, name, cmd, args, throw):
       
    72     ui.note(_("running hook %s: %s\n") % (name, cmd))
       
    73     env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
       
    74     r = util.system(cmd, environ=env, cwd=repo.root)
       
    75     if r:
       
    76         desc, r = util.explain_exit(r)
       
    77         if throw:
       
    78             raise util.Abort(_('%s hook %s') % (name, desc))
       
    79         ui.warn(_('warning: %s hook %s\n') % (name, desc))
       
    80     return r
       
    81 
       
    82 def hook(ui, repo, name, throw=False, **args):
       
    83     r = False
       
    84     hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks")
       
    85              if hname.split(".", 1)[0] == name and cmd]
       
    86     hooks.sort()
       
    87     for hname, cmd in hooks:
       
    88         if callable(cmd):
       
    89             r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
       
    90         elif cmd.startswith('python:'):
       
    91             r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
       
    92                             args, throw) or r
       
    93         else:
       
    94             r = _exthook(ui, repo, hname, cmd, args, throw) or r
       
    95     return r
       
    96