util: introduce util.debugstacktrace for showing a stack trace without crashing
This is often very handy when hacking/debugging.
Calling util.debugstacktrace('hey') from a place in hg will give something like:
hey at:
./hg:38 in <module>
/home/user/hgsrc/mercurial/dispatch.py:28 in run
/home/user/hgsrc/mercurial/dispatch.py:65 in dispatch
/home/user/hgsrc/mercurial/dispatch.py:88 in _runcatch
/home/user/hgsrc/mercurial/dispatch.py:740 in _dispatch
/home/user/hgsrc/mercurial/dispatch.py:514 in runcommand
/home/user/hgsrc/mercurial/dispatch.py:830 in _runcommand
/home/user/hgsrc/mercurial/dispatch.py:801 in checkargs
/home/user/hgsrc/mercurial/dispatch.py:737 in <lambda>
/home/user/hgsrc/mercurial/util.py:472 in check
...
--- a/contrib/lock-checker.py Tue Jan 07 22:29:57 2014 +0100
+++ b/contrib/lock-checker.py Sun Jan 12 23:28:21 2014 +0100
@@ -7,21 +7,12 @@
This currently only checks store locks, not working copy locks.
"""
import os
-import traceback
-
-def _warnstack(ui, msg, skip=1):
- '''issue warning with the message and the current stack, skipping the
- skip last entries'''
- ui.warn('%s at:\n' % msg)
- entries = traceback.extract_stack()[:-skip]
- fnmax = max(len(entry[0]) for entry in entries)
- for fn, ln, func, _text in entries:
- ui.warn(' %*s:%-4s in %s\n' % (fnmax, fn, ln, func))
+from mercurial import util
def _checklock(repo):
l = repo._lockref and repo._lockref()
if l is None or not l.held:
- _warnstack(repo.ui, 'missing lock', skip=2)
+ util.debugstacktrace('missing lock', skip=1)
def reposetup(ui, repo):
orig = repo.__class__
--- a/mercurial/util.py Tue Jan 07 22:29:57 2014 +0100
+++ b/mercurial/util.py Sun Jan 12 23:28:21 2014 +0100
@@ -1983,3 +1983,20 @@
self._hooks.sort(key=lambda x: x[0])
for source, hook in self._hooks:
hook(*args)
+
+def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr):
+ '''Writes a message to f (stderr) with a nicely formatted stacktrace.
+ Skips the 'skip' last entries.
+ It can be used everywhere and do intentionally not require an ui object.
+ Not be used in production code but very convenient while developing.
+ '''
+ f.write('%s at:\n' % msg)
+ entries = [('%s:%s' % (fn, ln), func)
+ for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
+ if entries:
+ fnmax = max(len(entry[0]) for entry in entries)
+ for fnln, func in entries:
+ f.write(' %-*s in %s\n' % (fnmax, fnln, func))
+
+# convenient shortcut
+dst = debugstacktrace
--- a/tests/test-debugcommands.t Tue Jan 07 22:29:57 2014 +0100
+++ b/tests/test-debugcommands.t Sun Jan 12 23:28:21 2014 +0100
@@ -23,3 +23,25 @@
uncompressed data size (min/max/avg) : 43 / 43 / 43
full revision size (min/max/avg) : 44 / 44 / 44
delta size (min/max/avg) : 0 / 0 / 0
+
+
+Test internal debugstacktrace command
+
+ $ cat > debugstacktrace.py << EOF
+ > from mercurial.util import debugstacktrace, dst, sys
+ > def f():
+ > dst('hello world')
+ > def g():
+ > f()
+ > debugstacktrace(skip=-5, f=sys.stdout)
+ > g()
+ > EOF
+ $ python debugstacktrace.py
+ hello world at:
+ debugstacktrace.py:7 in <module>
+ debugstacktrace.py:5 in g
+ debugstacktrace.py:3 in f
+ stacktrace at:
+ debugstacktrace.py:7 *in <module> (glob)
+ debugstacktrace.py:6 *in g (glob)
+ */util.py:* in debugstacktrace (glob)