# HG changeset patch # User Bryan O'Sullivan # Date 2009-11-12 22:05:52 # Node ID 9e7b2c49d25ddf8ba3a73bea2bf5b483911e773a # Parent 004bf1d6e6af84352065f2e5bbd5bc817a452ebd Make it possible to debug failed hook imports via use of --traceback Prior to this change, if a Python hook module failed to load (e.g. due to an import error or path problem), it was impossible to figure out why the error occurred, because the ImportErrors that got raised were caught but never displayed. If run with --traceback or ui.traceback=True, hg now prints tracebacks of both of the ImportError instances that get raised before it bails. diff --git a/mercurial/hook.py b/mercurial/hook.py --- a/mercurial/hook.py +++ b/mercurial/hook.py @@ -37,10 +37,18 @@ def _pythonhook(ui, repo, name, hname, f try: obj = __import__(modname) except ImportError: + e1 = sys.exc_type, sys.exc_value, sys.exc_traceback try: # extensions are loaded with hgext_ prefix obj = __import__("hgext_%s" % modname) except ImportError: + e2 = sys.exc_type, sys.exc_value, sys.exc_traceback + 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) raise util.Abort(_('%s hook is invalid ' '(import of "%s" failed)') % (hname, modname)) diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -15,7 +15,7 @@ import config, util, error class ui(object): def __init__(self, src=None): self._buffers = [] - self.quiet = self.verbose = self.debugflag = self._traceback = False + self.quiet = self.verbose = self.debugflag = self.tracebackflag = False self._reportuntrusted = True self._ocfg = config.config() # overlay self._tcfg = config.config() # trusted @@ -101,7 +101,7 @@ class ui(object): if self.verbose and self.quiet: self.quiet = self.verbose = False self._reportuntrusted = self.configbool("ui", "report_untrusted", True) - self._traceback = self.configbool('ui', 'traceback', False) + self.tracebackflag = self.configbool('ui', 'traceback', False) # update trust information self._trustusers.update(self.configlist('trusted', 'users')) @@ -337,13 +337,16 @@ class ui(object): return t - def traceback(self): + def traceback(self, exc=None): '''print exception traceback if traceback printing enabled. only to call in exception handler. returns true if traceback printed.''' - if self._traceback: - traceback.print_exc() - return self._traceback + if self.tracebackflag: + if exc: + traceback.print_exception(exc[0], exc[1], exc[2]) + else: + traceback.print_exc() + return self.tracebackflag def geteditor(self): '''return editor to use''' diff --git a/tests/test-hook b/tests/test-hook --- a/tests/test-hook +++ b/tests/test-hook @@ -248,4 +248,18 @@ echo "pre-commit.test = python:`pwd`/tes cd ../repo hg commit +cd ../../b +echo '# make sure --traceback works on hook import failure' +cat > importfail.py < .hg/hgrc +echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc + +echo a >> a +hg --traceback commit -Ama 2>&1 | grep '^\(exception\|Traceback\|ImportError\)' + exit 0 diff --git a/tests/test-hook.out b/tests/test-hook.out --- a/tests/test-hook.out +++ b/tests/test-hook.out @@ -163,3 +163,11 @@ hooks.commit.auto= # test python hook configured with python:[file]:[hook] syntax hook works nothing changed +# make sure --traceback works on hook import failure +exception from first failed import attempt: +Traceback (most recent call last): +ImportError: No module named somebogusmodule +exception from second failed import attempt: +Traceback (most recent call last): +ImportError: No module named hgext_importfail +Traceback (most recent call last):