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.
--- a/mercurial/hook.py Thu Nov 12 14:34:07 2009 -0600
+++ b/mercurial/hook.py Thu Nov 12 14:05:52 2009 -0800
@@ -37,10 +37,18 @@
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))
--- a/mercurial/ui.py Thu Nov 12 14:34:07 2009 -0600
+++ b/mercurial/ui.py Thu Nov 12 14:05:52 2009 -0800
@@ -15,7 +15,7 @@
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 @@
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 @@
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'''
--- a/tests/test-hook Thu Nov 12 14:34:07 2009 -0600
+++ b/tests/test-hook Thu Nov 12 14:05:52 2009 -0800
@@ -248,4 +248,18 @@
cd ../repo
hg commit
+cd ../../b
+echo '# make sure --traceback works on hook import failure'
+cat > importfail.py <<EOF
+import somebogusmodule
+# dereference something in the module to force demandimport to load it
+somebogusmodule.whatever
+EOF
+
+echo '[hooks]' > .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
--- a/tests/test-hook.out Thu Nov 12 14:34:07 2009 -0600
+++ b/tests/test-hook.out Thu Nov 12 14:05:52 2009 -0800
@@ -163,3 +163,11 @@
# 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):