##// END OF EJS Templates
profiling: move 'fp' closing logic into its own function...
profiling: move 'fp' closing logic into its own function We are about to make the logic more robust and reuse it in more place, we start by isolating what we have.

File last commit:

r32642:c032e137 default
r32804:c0b2c8f2 default
Show More
hook.py
269 lines | 9.6 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,
)
Matt Mackall
hooks: separate hook code into a separate module
r4622
Pierre-Yves David
hook: use "htype" as variable name in _pythonhook...
r31742 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
Augie Fackler
windows: check util.mainfrozen() instead of ad-hoc checks everywhere
r14941 if util.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:
Jordi Gutiérrez Hermoso
hooks: replace if-try-finally with a "with" statement...
r25328 obj = __import__(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
obj = __import__("hgext_%s" % 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:
Pierre-Yves David
hook: use "htype" as variable name in _pythonhook...
r31742 r = obj(ui=ui, repo=repo, hooktype=htype, **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: '
Pulkit Goyal
py3: convert exception to bytes to pass into ui.warn()...
r32642 '%s\n') % (hname, encoding.strtolocal(str(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):
Matt Mackall
hooks: separate hook code into a separate module
r4622 ui.note(_("running hook %s: %s\n") % (name, cmd))
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787
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()
Dan Villiom Podlaski Christiansen
hooks: sort any dictionaries set in the environment...
r13207 if isinstance(v, dict):
# make the dictionary element order stable across Python
# implementations
v = ('{' +
', '.join('%r: %r' % i for i in sorted(v.iteritems())) +
'}')
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 env['HG_' + k.upper()] = v
Matt Mackall
hooks: fix pre- and post- hooks specified in .hg/hgrc...
r5869 if repo:
cwd = repo.root
else:
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 cwd = pycompat.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:
Adrian Buehlmann
rename explain_exit to explainexit
r14234 desc, r = util.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 Zuba
hooks: prioritize run order of hooks...
r15896 if not name.startswith('priority'):
priority = ui.configint('hooks', 'priority.%s' % name, 0)
Pierre-Yves David
hook: small refactor to store hooks as dict instead of list...
r28936 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
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):
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
py3: bulk replace sys.stdin/out/err by util's...
r30473 stdoutno = util.stdout.fileno()
stderrno = util.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
py3: bulk replace sys.stdin/out/err by util's...
r30473 util.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):
Pierre-Yves David
hook: use 'htype' in 'runhooks'...
r31744 r, raised = _pythonhook(ui, repo, htype, hname, cmd, args,
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()
Pierre-Yves David
hook: use 'htype' in 'runhooks'...
r31744 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: forcibly flush stderr for Windows test stability...
r24716
# 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
py3: bulk replace sys.stdin/out/err by util's...
r30473 util.stderr.flush()
Jesse Long
hooks: restore io correctly on exception
r7416 finally:
Sune Foldager
hook: only redirect stdout if it and stderr are valid files...
r9658 if _redirect and oldstdout >= 0:
Yuya Nishihara
py3: bulk replace sys.stdin/out/err by util's...
r30473 util.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