mercurial/hook.py
author Gilles Moris <gilles.moris@free.fr>
Thu, 25 Dec 2008 17:49:08 +0100
changeset 7544 c5e37dc38a52
parent 7416 196b05a548d0
child 7644 182b7114d35a
permissions -rw-r--r--
test-hgweb-*: replace "XX years ago" by a fixed string in get-with-headers.py Note that other sed replacement commands are also filtering get-with-headers.py, but they seem to be more test specific, hence not handled here.

# hook.py - hook support for mercurial
#
# Copyright 2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

from i18n import _
import util, os, sys

def _pythonhook(ui, repo, name, hname, funcname, args, throw):
    '''call python hook. hook is callable object, looked up as
    name in python module. if callable returns "true", hook
    fails, else passes. if hook raises exception, treated as
    hook failure. exception propagates if throw is "true".

    reason for "true" meaning "hook failed" is so that
    unmodified commands (e.g. mercurial.commands.update) can
    be run as hooks without wrappers to convert return values.'''

    ui.note(_("calling hook %s: %s\n") % (hname, funcname))
    obj = funcname
    if not callable(obj):
        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:
            try:
                # extensions are loaded with hgext_ prefix
                obj = __import__("hgext_%s" % 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:
            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):
            ui.warn(_('error: %s hook failed: %s\n') %
                         (hname, exc.args[0]))
        else:
            ui.warn(_('error: %s hook raised an exception: '
                           '%s\n') % (hname, exc))
        if throw:
            raise
        ui.print_exc()
        return True
    if r:
        if throw:
            raise util.Abort(_('%s hook failed') % hname)
        ui.warn(_('warning: %s hook failed\n') % hname)
    return r

def _exthook(ui, repo, name, cmd, args, throw):
    ui.note(_("running hook %s: %s\n") % (name, cmd))
    env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
    if repo:
        cwd = repo.root
    else:
        cwd = os.getcwd()
    r = util.system(cmd, environ=env, cwd=cwd)
    if r:
        desc, r = util.explain_exit(r)
        if throw:
            raise util.Abort(_('%s hook %s') % (name, desc))
        ui.warn(_('warning: %s hook %s\n') % (name, desc))
    return r

_redirect = False
def redirect(state):
    global _redirect
    _redirect = state

def hook(ui, repo, name, throw=False, **args):
    r = False

    if _redirect:
        # temporarily redirect stdout to stderr
        oldstdout = os.dup(sys.__stdout__.fileno())
        os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())

    try:
        for hname, cmd in util.sort(ui.configitems('hooks')):
            if hname.split('.')[0] != name or not cmd:
                continue
            if callable(cmd):
                r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
            elif cmd.startswith('python:'):
                r = _pythonhook(ui, repo, name, hname, cmd[7:].strip(),
                                args, throw) or r
            else:
                r = _exthook(ui, repo, hname, cmd, args, throw) or r
    finally:
        if _redirect:
            os.dup2(oldstdout, sys.__stdout__.fileno())
            os.close(oldstdout)

    return r