##// END OF EJS Templates
commands: drop support for legacy ^cmd registration (API)
commands: drop support for legacy ^cmd registration (API)

File last commit:

r41007:8c8fcb38 default
r42523:fa4b13e8 default
Show More
hook.py
287 lines | 10.1 KiB | text/x-python | PythonLexer
Matt Mackall
hooks: separate hook code into a separate module
r4622 # hook.py - hook support for mercurial
#
# Copyright 2007 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
hooks: separate hook code into a separate module
r4622
Gregory Szorc
hook: use absolute_import
r25953 from __future__ import absolute_import
import os
import sys
from .i18n import _
from . import (
demandimport,
Pulkit Goyal
py3: convert exception to bytes to pass into ui.warn()...
r32642 encoding,
Gregory Szorc
hook: use absolute_import
r25953 error,
extensions,
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 pycompat,
Gregory Szorc
hook: use absolute_import
r25953 util,
)
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 from .utils import (
procutil,
Augie Fackler
hook: use stringutil.pprint instead of reinventing it...
r37769 stringutil,
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 )
Matt Mackall
hooks: separate hook code into a separate module
r4622
hindlemail
filemerge: support specifying a python function to custom merge-tools...
r38052 def pythonhook(ui, repo, htype, hname, funcname, args, throw):
Matt Mackall
hooks: separate hook code into a separate module
r4622 '''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.'''
Augie Fackler
hook: restore use of callable() since it was readded in Python 3.2
r21797 if callable(funcname):
Mads Kiilerich
hooks: for python hooks, consistently use __name__ etc as name, not the repr...
r20548 obj = funcname
Pulkit Goyal
py3: ensure that we don't concat bytes and str and the end result is bytes...
r32614 funcname = pycompat.sysbytes(obj.__module__ + r"." + obj.__name__)
Mads Kiilerich
hooks: for python hooks, consistently use __name__ etc as name, not the repr...
r20548 else:
Matt Mackall
hooks: separate hook code into a separate module
r4622 d = funcname.rfind('.')
if d == -1:
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 raise error.HookLoadError(
Siddharth Agarwal
hook: even fewer parentheses for load errors...
r28106 _('%s hook is invalid: "%s" not in a module')
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 % (hname, funcname))
Matt Mackall
hooks: separate hook code into a separate module
r4622 modname = funcname[:d]
Sune Foldager
hook: fix bug (reuse of variable) introduced in 872d49dd577a...
r10103 oldpaths = sys.path
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 if procutil.mainfrozen():
Steve Borho
hook: fix full path imports on Windows (issue1779)...
r9332 # binary installs require sys.path manipulation
Sune Foldager
hook: fix bug (reuse of variable) introduced in 872d49dd577a...
r10103 modpath, modfile = os.path.split(modname)
if modpath and modfile:
sys.path = sys.path[:] + [modpath]
modname = modfile
Jordi Gutiérrez Hermoso
hooks: replace if-try-finally with a "with" statement...
r25328 with demandimport.deactivated():
Matt Mackall
hooks: separate hook code into a separate module
r4622 try:
Gregory Szorc
py3: use system strings when calling __import__...
r36125 obj = __import__(pycompat.sysstr(modname))
Siddharth Agarwal
hook: don't crash on syntax errors in python hooks...
r28109 except (ImportError, SyntaxError):
Siddharth Agarwal
hook: use sys.exc_info rather than the deprecated equivalents...
r28078 e1 = sys.exc_info()
Jordi Gutiérrez Hermoso
hooks: replace if-try-finally with a "with" statement...
r25328 try:
# extensions are loaded with hgext_ prefix
Gregory Szorc
py3: use system strings when calling __import__...
r36125 obj = __import__(r"hgext_%s" % pycompat.sysstr(modname))
Siddharth Agarwal
hook: don't crash on syntax errors in python hooks...
r28109 except (ImportError, SyntaxError):
Siddharth Agarwal
hook: use sys.exc_info rather than the deprecated equivalents...
r28078 e2 = sys.exc_info()
Jordi Gutiérrez Hermoso
hooks: replace if-try-finally with a "with" statement...
r25328 if ui.tracebackflag:
ui.warn(_('exception from first failed import '
'attempt:\n'))
ui.traceback(e1)
if ui.tracebackflag:
ui.warn(_('exception from second failed import '
'attempt:\n'))
ui.traceback(e2)
Siddharth Agarwal
hook: for python hook ImportErrors, add note to run with --traceback...
r28080
if not ui.tracebackflag:
tracebackhint = _(
'run with --traceback for stack trace')
else:
tracebackhint = None
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 raise error.HookLoadError(
Siddharth Agarwal
hook: fewer parentheses for hook load errors...
r28079 _('%s hook is invalid: import of "%s" failed') %
Siddharth Agarwal
hook: for python hook ImportErrors, add note to run with --traceback...
r28080 (hname, modname), hint=tracebackhint)
Steve Borho
hook: fix full path imports on Windows (issue1779)...
r9332 sys.path = oldpaths
Matt Mackall
hooks: separate hook code into a separate module
r4622 try:
for p in funcname.split('.')[1:]:
obj = getattr(obj, p)
Benoit Boissinot
remove unused variables
r7280 except AttributeError:
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 raise error.HookLoadError(
Siddharth Agarwal
hook: fewer parentheses for hook load errors...
r28079 _('%s hook is invalid: "%s" is not defined')
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 % (hname, funcname))
Augie Fackler
hook: restore use of callable() since it was readded in Python 3.2
r21797 if not callable(obj):
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 raise error.HookLoadError(
Siddharth Agarwal
hook: fewer parentheses for hook load errors...
r28079 _('%s hook is invalid: "%s" is not callable')
Siddharth Agarwal
hook: raise a separate exception for when loading a hook fails...
r26692 % (hname, funcname))
Mads Kiilerich
hooks: move logging of hook name to after we have found the hook...
r20547
ui.note(_("calling hook %s: %s\n") % (hname, funcname))
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 starttime = util.timer()
Mads Kiilerich
hooks: move logging of hook name to after we have found the hook...
r20547
Matt Mackall
hooks: separate hook code into a separate module
r4622 try:
Pulkit Goyal
py3: handle keyword arguments correctly in hook.py...
r35358 r = obj(ui=ui, repo=repo, hooktype=htype, **pycompat.strkwargs(args))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except Exception as exc:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 if isinstance(exc, error.Abort):
Matt Mackall
hooks: use try/except/finally
r25084 ui.warn(_('error: %s hook failed: %s\n') %
(hname, exc.args[0]))
else:
ui.warn(_('error: %s hook raised an exception: '
Yuya Nishihara
py3: use forcebytestr() to stringify hook exception...
r41007 '%s\n') % (hname, stringutil.forcebytestr(exc)))
Matt Mackall
hooks: use try/except/finally
r25084 if throw:
Matt Mackall
hooks: separate hook code into a separate module
r4622 raise
Siddharth Agarwal
hook: for python hook exceptions, add note to run with --traceback...
r28108 if not ui.tracebackflag:
ui.warn(_('(run with --traceback for stack trace)\n'))
Matt Mackall
hooks: use try/except/finally
r25084 ui.traceback()
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 return True, True
Idan Kamara
hooks: redirect stdout/err/in to the ui descriptors when calling python hooks...
r14889 finally:
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 duration = util.timer() - starttime
Durham Goode
blackbox: do not translate the log messages...
r18691 ui.log('pythonhook', 'pythonhook-%s: %s finished in %0.2f seconds\n',
Pierre-Yves David
hook: use "htype" as variable name in _pythonhook...
r31742 htype, funcname, duration)
Matt Mackall
hooks: separate hook code into a separate module
r4622 if r:
if throw:
Pierre-Yves David
hook: raise a more specialized HookAbort exception when a hook fails...
r23415 raise error.HookAbort(_('%s hook failed') % hname)
Matt Mackall
hooks: separate hook code into a separate module
r4622 ui.warn(_('warning: %s hook failed\n') % hname)
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 return r, False
Matt Mackall
hooks: separate hook code into a separate module
r4622
Pierre-Yves David
hook: provide hook type information to external hook...
r31746 def _exthook(ui, repo, htype, name, cmd, args, throw):
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 starttime = util.timer()
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 env = {}
FUJIWARA Katsunori
hook: centralize passing HG_PENDING to external hook process...
r26751
# make in-memory changes visible to external process
Sietse Brouwer
dirstate: don't write repo.currenttransaction to repo.dirstate if repo...
r27228 if repo is not None:
tr = repo.currenttransaction()
repo.dirstate.write(tr)
if tr and tr.writepending():
env['HG_PENDING'] = repo.root
Pierre-Yves David
hook: provide hook type information to external hook...
r31746 env['HG_HOOKTYPE'] = htype
Pierre-Yves David
hook: add hook name information to external hook...
r31747 env['HG_HOOKNAME'] = name
FUJIWARA Katsunori
hook: centralize passing HG_PENDING to external hook process...
r26751
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 for k, v in args.iteritems():
Augie Fackler
hook: restore use of callable() since it was readded in Python 3.2
r21797 if callable(v):
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 v = v()
Augie Fackler
hook: also use pprint on lists for stable output on py2/3...
r37770 if isinstance(v, (dict, list)):
Yuya Nishihara
stringutil: flip the default of pprint() to bprefix=False...
r37961 v = stringutil.pprint(v)
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 env['HG_' + k.upper()] = v
Matt Harbison
hook: disable the shell to native command translation by default...
r38745 if ui.configbool('hooks', 'tonative.%s' % name, False):
Matt Harbison
hook: only print the note about native cmd translation if it actually changes...
r38746 oldcmd = cmd
Matt Harbison
hook: add support for disabling the shell to native command translation...
r38648 cmd = procutil.shelltonative(cmd, env)
Matt Harbison
hook: only print the note about native cmd translation if it actually changes...
r38746 if cmd != oldcmd:
ui.note(_('converting hook "%s" to native\n') % name)
Matt Harbison
hooks: allow Unix style environment variables on external Windows hooks...
r38503
ui.note(_("running hook %s: %s\n") % (name, cmd))
Matt Mackall
hooks: fix pre- and post- hooks specified in .hg/hgrc...
r5869 if repo:
cwd = repo.root
else:
Matt Harbison
py3: rename pycompat.getcwd() to encoding.getcwd() (API)...
r39843 cwd = encoding.getcwd()
Simon Farnsworth
hook: give exthooks tags for blocking time...
r31205 r = ui.system(cmd, environ=env, cwd=cwd, blockedtag='exthook-%s' % (name,))
Durham Goode
blackbox: logs python and extension hooks via ui.log()...
r18671
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 duration = util.timer() - starttime
Durham Goode
blackbox: do not translate the log messages...
r18691 ui.log('exthook', 'exthook-%s: %s finished in %0.2f seconds\n',
Durham Goode
blackbox: logs python and extension hooks via ui.log()...
r18671 name, cmd, duration)
Matt Mackall
hooks: separate hook code into a separate module
r4622 if r:
Yuya Nishihara
procutil: make explainexit() simply return a message (API)...
r37481 desc = procutil.explainexit(r)
Matt Mackall
hooks: separate hook code into a separate module
r4622 if throw:
Pierre-Yves David
hook: raise a more specialized HookAbort exception when a hook fails...
r23415 raise error.HookAbort(_('%s hook %s') % (name, desc))
Matt Mackall
hooks: separate hook code into a separate module
r4622 ui.warn(_('warning: %s hook %s\n') % (name, desc))
return r
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 # represent an untrusted hook command
_fromuntrusted = object()
Matt Zuba
hooks: prioritize run order of hooks...
r15896 def _allhooks(ui):
Pierre-Yves David
hook: split config reading further...
r28937 """return a list of (hook-id, cmd) pairs sorted by priority"""
hooks = _hookitems(ui)
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 # Be careful in this section, propagating the real commands from untrusted
# sources would create a security vulnerability, make sure anything altered
# in that section uses "_fromuntrusted" as its command.
untrustedhooks = _hookitems(ui, _untrusted=True)
for name, value in untrustedhooks.items():
trustedvalue = hooks.get(name, (None, None, name, _fromuntrusted))
if value != trustedvalue:
(lp, lo, lk, lv) = trustedvalue
hooks[name] = (lp, lo, lk, _fromuntrusted)
# (end of the security sensitive section)
Pierre-Yves David
hook: split config reading further...
r28937 return [(k, v) for p, o, k, v in sorted(hooks.values())]
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 def _hookitems(ui, _untrusted=False):
Pierre-Yves David
hook: split config reading further...
r28937 """return all hooks items ready to be sorted"""
Pierre-Yves David
hook: small refactor to store hooks as dict instead of list...
r28936 hooks = {}
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 for name, cmd in ui.configitems('hooks', untrusted=_untrusted):
Matt Harbison
hook: add support for disabling the shell to native command translation...
r38648 if name.startswith('priority.') or name.startswith('tonative.'):
continue
priority = ui.configint('hooks', 'priority.%s' % name, 0)
hooks[name] = (-priority, len(hooks), name, cmd)
Pierre-Yves David
hook: split config reading further...
r28937 return hooks
Matt Zuba
hooks: prioritize run order of hooks...
r15896
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833 _redirect = False
def redirect(state):
Alexis S. L. Carvalho
hook.py: fix redirections introduced by 323b9c55b328...
r6266 global _redirect
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833 _redirect = state
Boris Feld
hook: add a 'hashook' function to test for hook existence...
r34688 def hashook(ui, htype):
"""return True if a hook is configured for 'htype'"""
if not ui.callhooks:
return False
for hname, cmd in _allhooks(ui):
if hname.split('.')[0] == htype and cmd:
return True
return False
Pierre-Yves David
hook: use 'htype' in 'hook'...
r31745 def hook(ui, repo, htype, throw=False, **args):
Idan Kamara
ui: add a variable to control whether hooks should be called...
r17048 if not ui.callhooks:
return False
Siddharth Agarwal
hook: factor out determination of hooks from running them...
r26737 hooks = []
for hname, cmd in _allhooks(ui):
Pierre-Yves David
hook: use 'htype' in 'hook'...
r31745 if hname.split('.')[0] == htype and cmd:
Siddharth Agarwal
hook: factor out determination of hooks from running them...
r26737 hooks.append((hname, cmd))
Pierre-Yves David
hook: use 'htype' in 'hook'...
r31745 res = runhooks(ui, repo, htype, hooks, throw=throw, **args)
Siddharth Agarwal
hook.runhooks: return a dict of result values...
r26738 r = False
for hname, cmd in hooks:
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 r = res[hname][0] or r
Siddharth Agarwal
hook.runhooks: return a dict of result values...
r26738 return r
Siddharth Agarwal
hook: factor out determination of hooks from running them...
r26737
Pierre-Yves David
hook: use 'htype' in 'runhooks'...
r31744 def runhooks(ui, repo, htype, hooks, throw=False, **args):
Pulkit Goyal
py3: convert keys of kwargs back to bytes using pycompat.byteskwargs()
r32897 args = pycompat.byteskwargs(args)
Siddharth Agarwal
hook.runhooks: return a dict of result values...
r26738 res = {}
Sune Foldager
hook: only redirect stdout if it and stderr are valid files...
r9658 oldstdout = -1
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833
Jesse Long
hooks: restore io correctly on exception
r7416 try:
Siddharth Agarwal
hook: factor out determination of hooks from running them...
r26737 for hname, cmd in hooks:
Matt Mackall
hooks: delay I/O redirection until we actually run a hook (issue3711)...
r17963 if oldstdout == -1 and _redirect:
try:
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 stdoutno = procutil.stdout.fileno()
stderrno = procutil.stderr.fileno()
Matt Mackall
hooks: delay I/O redirection until we actually run a hook (issue3711)...
r17963 # temporarily redirect stdout to stderr, if possible
if stdoutno >= 0 and stderrno >= 0:
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 procutil.stdout.flush()
Matt Mackall
hooks: delay I/O redirection until we actually run a hook (issue3711)...
r17963 oldstdout = os.dup(stdoutno)
os.dup2(stderrno, stdoutno)
Matt Mackall
hooks: be even more forgiven of non-fd descriptors (issue3711)...
r17964 except (OSError, AttributeError):
# files seem to be bogus, give up on redirecting (WSGI, etc)
Matt Mackall
hooks: delay I/O redirection until we actually run a hook (issue3711)...
r17963 pass
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 if cmd is _fromuntrusted:
if throw:
raise error.HookAbort(
Pierre-Yves David
hook: fix name used in untrusted message...
r31743 _('untrusted hook %s not executed') % hname,
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 hint = _("see 'hg help config.trusted'"))
Pierre-Yves David
hook: fix name used in untrusted message...
r31743 ui.warn(_('warning: untrusted hook %s not executed\n') % hname)
Pierre-Yves David
hook: report untrusted hooks as failure (issue5110) (BC)...
r28938 r = 1
raised = False
elif callable(cmd):
hindlemail
filemerge: support specifying a python function to custom merge-tools...
r38052 r, raised = pythonhook(ui, repo, htype, hname, cmd, args,
Pierre-Yves David
hook: use 'htype' in 'runhooks'...
r31744 throw)
Jesse Long
hooks: restore io correctly on exception
r7416 elif cmd.startswith('python:'):
Steve Borho
hook: fix full path imports on Windows (issue1779)...
r9332 if cmd.count(':') >= 2:
path, cmd = cmd[7:].rsplit(':', 1)
Alexander Solovyov
hook: assume relative path to hook is given from repo root
r13118 path = util.expandpath(path)
Matt Mackall
hook: fix import path handling for repo=None
r13119 if repo:
path = os.path.join(repo.root, path)
Simon Heimberg
hooks: print out more information when loading a python hook fails...
r17217 try:
mod = extensions.loadpath(path, 'hghook.%s' % hname)
except Exception:
ui.write(_("loading %s hook failed:\n") % hname)
raise
Alexander Solovyov
ability to load hooks from arbitrary python module
r7916 hookfn = getattr(mod, cmd)
else:
hookfn = cmd[7:].strip()
hindlemail
filemerge: support specifying a python function to custom merge-tools...
r38052 r, raised = pythonhook(ui, repo, htype, hname, hookfn, args,
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 throw)
Jesse Long
hooks: restore io correctly on exception
r7416 else:
Pierre-Yves David
hook: provide hook type information to external hook...
r31746 r = _exthook(ui, repo, htype, hname, cmd, args, throw)
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 raised = False
Siddharth Agarwal
hook.runhooks: return a dict of result values...
r26738
Siddharth Agarwal
hook: for python hooks, also return whether an exception was raised...
r26739 res[hname] = r, raised
Matt Harbison
hook: ensure stderr is flushed when an exception is raised, for test stability...
r36859 finally:
# The stderr is fully buffered on Windows when connected to a pipe.
# A forcible flush is required to make small stderr data in the
# remote side available to the client immediately.
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 procutil.stderr.flush()
Matt Harbison
hook: forcibly flush stderr for Windows test stability...
r24716
Sune Foldager
hook: only redirect stdout if it and stderr are valid files...
r9658 if _redirect and oldstdout >= 0:
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 procutil.stdout.flush() # write hook output to stderr fd
Sune Foldager
hook: only redirect stdout if it and stderr are valid files...
r9658 os.dup2(oldstdout, stdoutno)
Jesse Long
hooks: restore io correctly on exception
r7416 os.close(oldstdout)
Alexis S. L. Carvalho
hook.py: fix redirections introduced by 323b9c55b328...
r6266
Siddharth Agarwal
hook.runhooks: return a dict of result values...
r26738 return res