traceback shows up until decorator
decorator, python, traceback
Solution
Ah! Ok, now this is an interesting question!
Here is is the same approximate function, but grabbing the 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 it's true: no higher stack frames are included in the traceback that's printed:
$ 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 this down a bit, I suspect it's happening because the stack frame only includes stack information up until the most recent `try/except` block… So we should be able to recreate this without the 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
Ah ha! Now, on reflection, this does make sense in a certain kind of way: at this point, the exception has only encountered two stack frames: that of `inner()` and that of `outer()` — the exception doesn't yet know from whence `outer()` was called.
So, to get the complete stack, you'll need to combine the current stack with the exception's 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:
- http://docs.python.org/2/library/sys.html
- http://docs.python.org/2/library/traceback.html
Problem
This nice little Python decorator can configurably disabled decorated functions: ``` enabled = get_bool_from_config() def run_if_enabled(fn): def wrapped(*args, **kwargs): try: return fn(*args, **kwargs) if enabled else None except Exception: log.exception('') return None return wrapped ``` alas, if an exception is raised within `fn()` the traceback shows only up to the wrapper: ``` Traceback (most recent call last): File "C:\my_proj\run.py", line 46, in wrapped return fn(*args, **kwargs) if enabled else None File "C:\my_proj\run.py", line 490, in a_decorated_function some_dict['some_value'] KeyError: 'some_value' ``` - Why? - Can I workaround to see the full traceback?