Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

traceback.print_exc() shows incomplete stack trace

Tags:

python

The traceback module is great for capturing and handling exceptions, but in the following example it seems to capture an incomplete stack from the most recent exception.

Consider two files, one say "mymod.py":

import sys, traceback

def func():
    try:
        otherfunc()
    except:
        raise
        #traceback.print_exc()

def otherfunc():
    raise Exception( 'fake' )

And another one say "myfile.py":

from mymod import *
func()

Running myfile.py gives:

Traceback (most recent call last):
  File "myfile.py", line 2, in <module>
    func()
  File "/Users/rrdrake/temp/mymod.py", line 5, in func
    otherfunc()
  File "/Users/rrdrake/temp/mymod.py", line 11, in otherfunc
    raise Exception( 'fake' )

Now change mymod.py to comment out the "raise" and uncomment the print_exc line. Then we get

Traceback (most recent call last):
  File "/Users/rrdrake/temp/mymod.py", line 5, in func
    otherfunc()
  File "/Users/rrdrake/temp/mymod.py", line 11, in otherfunc
    raise Exception( 'fake' )
Exception: fake

Notice that print_exc() does not include the myfile.py frame while re-raising does. How can I make print_exc() include the origin calling frame?

Note that if I add a traceback.print_stack() in the except block, it does include the myfile.py frame, so the information seems to be available.

like image 371
Rich Drake Avatar asked Sep 01 '25 02:09

Rich Drake


1 Answers

In your first case, the exception from your raise call bubbles up at the top level of the script. Python calls sys.excepthook(), which displays the full traceback.

In your second case, the exception is caught and print_exc() is used to print the exception, and this stops at the calling function (otherfunc() in your case) so you don't get the full traceback.

You can change that by using your own traceback/exception print function, something along those lines:

def print_full_stack(tb=None):
    if tb is None:
        tb = sys.exc_info()[2]

    print 'Traceback (most recent call last):'
    for item in reversed(inspect.getouterframes(tb.tb_frame)[1:]):
        print ' File "{1}", line {2}, in {3}\n'.format(*item),
        for line in item[4]:
            print ' ' + line.lstrip(),
    for item in inspect.getinnerframes(tb):
        print ' File "{1}", line {2}, in {3}\n'.format(*item),
        for line in item[4]:
            print ' ' + line.lstrip(),

Just replace traceback.print_exc() with print_full_stack(). This function involves the inspect module to get code frames.

You can read this blog for much more information: http://blog.dscpl.com.au/2015/03/generating-full-stack-traces-for.html

Beware, the code in the above article has some indentation errors...

like image 116
mguijarr Avatar answered Sep 02 '25 15:09

mguijarr