views:

79

answers:

4

When I get a runtime exception from the standard library, it's almost always a problem in my code and not in the library code. Is there a way to truncate the exception stack trace so that it doesn't show the guts of the library package?

For example, I would like to get this:

Traceback (most recent call last):
  File "./lmd3-mkhead.py", line 71, in <module>
    main()
  File "./lmd3-mkhead.py", line 66, in main
    create()
  File "./lmd3-mkhead.py", line 41, in create
    headver1[depotFile]=rev
TypeError: Data values must be of type string or None.

and not this:

Traceback (most recent call last):
  File "./lmd3-mkhead.py", line 71, in <module>
    main()
  File "./lmd3-mkhead.py", line 66, in main
    create()
  File "./lmd3-mkhead.py", line 41, in create
    headver1[depotFile]=rev
  File "/usr/anim/modsquad/oses/fc11/lib/python2.6/bsddb/__init__.py", line 276, in __setitem__
    _DeadlockWrap(wrapF)  # self.db[key] = value
  File "/usr/anim/modsquad/oses/fc11/lib/python2.6/bsddb/dbutils.py", line 68, in DeadlockWrap
    return function(*_args, **_kwargs)
  File "/usr/anim/modsquad/oses/fc11/lib/python2.6/bsddb/__init__.py", line 275, in wrapF
    self.db[key] = value
TypeError: Data values must be of type string or None.

update: added an answer with the code, thanks to the pointer from Alex.

A: 

Put an unqualified try...except at the top of your code (ie: in your "main") or set sys.excepthook. You can then format the stack trace however you'd like.

Laurence Gonsalves
A: 

The Traceback library is probably what you want. Here's one example that might help:

import traceback

try:
    your_main()
except:
    lines = traceback.format_exc()
    print lines[:lines.find('File "/usr')]

(This obviously won't work if there's an exception outside the library, and might not exactly fit your needs, but it's one way of using the traceback library)

Michael Mrozek
+5  A: 

The traceback module in Python's standard library lets you emit error tracebacks in a way that accords to your liking, while an exception is propagating. You can use this power either in the except leg of a try/except statement, or in a function you've installed as sys.excepthook, which gets called if and when an exception propagates all the way; quoting the docs:

In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.

Here's a simple, artificial example:

>>> import sys
>>> import traceback
>>> def f(n):
...   if n<=0: raise ZeroDivisionError
...   f(n-1)
... 
>>> def excepthook(type, value, tb):
...   traceback.print_exception(type, value, tb, 3)
... 
>>> sys.excepthook = excepthook
>>> f(8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in f
  File "<stdin>", line 3, in f
ZeroDivisionError

as you see, without needing a try/except, you can easily limit the traceback to (for example) the first three levels -- even though we know by design that there were 9 nested levels when the exception was raised.

You want something more sophisticated than a simple limit on levels, so you'll need to call traceback.format_exception, which gives you a list of lines rather than printing it, then "prune" from that list the lines that are about modules you never want to see in your tracebacks, and finally emit the remaining lines (typically to sys.stderr, but, whatever!-).

Alex Martelli
pure distilled wisdom, mixed with a dash of style... thanks Alex!
Mark Harrison
@Mark, you're welcome -- thanks for the kudos!
Alex Martelli
+2  A: 

Thanks to the pointer from Alex, here's teh codez:

def trimmedexceptions(type, value, tb, pylibdir=None, lev=None):
    """trim system packages from the exception printout"""
    if pylibdir is None:
        import traceback, distutils.sysconfig
        pylibdir = distutils.sysconfig.get_python_lib(1,1)
        nlev = trimmedexceptions(type, value, tb, pylibdir, 0)
        traceback.print_exception(type, value, tb, nlev)
    else:
        fn = tb.tb_frame.f_code.co_filename
        if tb.tb_next is None or fn.startswith(pylibdir):
            return lev
        else:
            return trimmedexceptions(type, value, tb.tb_next, pylibdir, lev+1)

import sys
sys.excepthook=trimmedexceptions

# --- test code ---

def f1(): f2()
def f2(): f3()
def f3():
    import xmlrpclib
    proxy = xmlrpclib.ServerProxy('http://nosuchserver')
    proxy.f()

f1()

Which yields this stack trace:

Traceback (most recent call last):
  File "./tsttraceback.py", line 47, in <module>
    f1()
  File "./tsttraceback.py", line 40, in f1
    def f1(): f2()
  File "./tsttraceback.py", line 41, in f2
    def f2(): f3()
  File "./tsttraceback.py", line 45, in f3
    proxy.f()
gaierror: [Errno -2] Name or service not known
Mark Harrison