Oh! Ok, now this is an interesting question!
Here is the same approximate function, but sys.exc_info() exception directly from sys.exc_info() :
import sys import traceback def save_if_allowed(fn): def wrapped(*args, **kwargs): try: return fn(*args, **kwargs) if enabled else None except Exception: print "The exception:" print "".join(traceback.format_exception(*sys.exc_info())) return None return wrapped @save_if_allowed def stuff(): raise Exception("stuff") def foo(): stuff() foo()
And this is true: in the trace trace that was printed, no higher stack frames were added:
$ python test.py
The exception:
Traceback (most recent call last):
File "x.py", line 21, in wrapped
return fn (* args, ** kwargs) if enabled else None
File "x.py", line 29, in stuff
raise Exception ("stuff")
Exception: stuff
Now, to narrow it down a bit, I suspect this is happening because the stack frame only includes stack information up to the very last try/except block ... Therefore, we should be able to recreate this without a decorator:
$ cat test.py def inner(): raise Exception("inner") def outer(): try: inner() except Exception: print "".join(traceback.format_exception(*sys.exc_info())) def caller(): outer() caller() $ python test.py Traceback (most recent call last): File "x.py", line 42, in outer inner() File "x.py", line 38, in inner raise Exception("inner") Exception: inner
Yeah! Now, when reflected, this makes sense in a certain way: at the moment, an exception is encountered only with two stack frames: with inner() and outer() - the exception is not yet known where outer() from.
So, to get the full stack, you need to combine the current stack with the exception stack:
$ cat test.py def inner(): raise Exception("inner") def outer(): try: inner() except Exception: exc_info = sys.exc_info() stack = traceback.extract_stack() tb = traceback.extract_tb(exc_info[2]) full_tb = stack[:-1] + tb exc_line = traceback.format_exception_only(*exc_info[:2]) print "Traceback (most recent call last):" print "".join(traceback.format_list(full_tb)), print "".join(exc_line) def caller(): outer() caller() $ python test.py Traceback (most recent call last): File "test.py", line 56, in <module> caller() File "test.py", line 54, in caller outer() File "test.py", line 42, in outer inner() File "test.py", line 38, in inner raise Exception("inner") Exception: inner
See also: