Retrieve trace information from an exception object

For an exception object (of unknown origin), is there a way to get a trace? I have a code like this:

def stuff(): try: ..... return useful except Exception as e: return e result = stuff() if isinstance(result, Exception): result.traceback <-- How? 

How can I extract a trace from an Exception object when it is?

+93
python debugging exception-handling
Jul 10 2018-12-12T00:
source share
3 answers

The answer to this question depends on the version of Python used.

In Python 3

It's simple: exceptions are provided with the __traceback__ attribute, which contains the trace. This attribute is also writable and can be conveniently set using the with_traceback exception with_traceback :

 raise Exception("foo occurred").with_traceback(tracebackobj) 

These functions are minimally described as part of raise .

All thanks for this part of the answer should go to Vyctor, who first published this information . I only include it here because this answer is stuck at the top, and Python 3 is becoming more and more common.

In Python 2

This is annoyingly complicated. The problem with tracking is that they have links to stack frames, and in the stack frame there are links to traces that have links to stack frames that have links to ... You have an idea. This causes problems for the garbage collector (thanks ecatmur for pointing this out first.)

A good way to solve this would be to interrupt the cycle after exiting the except clause, which makes Python 3. The Python 2 solution is much uglier: you are provided with a special function sys.exc_info() that works only inside the except clause. It returns a tuple containing the exception, type of exception, and trace for any exception that is currently being processed.

So, if you are inside the except clause, you can use the output of sys.exec_info() along with the traceback module to do various useful things:

 >>> import sys, traceback >>> def raise_exception(): ... try: ... raise Exception ... except Exception: ... ex_type, ex, tb = sys.exc_info() ... traceback.print_tb(tb) ... finally: ... del tb ... >>> raise_exception() File "<stdin>", line 3, in raise_exception 

But as your change indicates, you are trying to get a trace that would be printed if your exception had not been processed after it was already processed. It is much more complicated. Unfortunately, sys.exc_info returns (None, None, None) when an exception is thrown. Other related sys attributes do not help either. sys.exc_traceback deprecated and undefined when an exception is thrown; sys.last_traceback seems perfect, but it seems to be detected only during interactive sessions.

If you can control how an exception is thrown, you can use inspect and a custom exception to store some information. But I'm not quite sure how this will work.

Honestly, catching and throwing an exception is a bit of an unusual thing. This may be a sign that you still need to refactor.

+77
Jul 10 2018-12-12T00:
source share
— -

Since Python 3.0 [PEP 3109], the built-in Exception class has the __traceback__ attribute, which contains a traceback object (since Python 3.2.3):

 >>> try: ... raise Exception() ... except Exception as e: ... tb = e.__traceback__ ... >>> tb <traceback object at 0x00000000022A9208> 

The problem is that after Googling __traceback__ for a while I found only a few articles, but none of them describes whether you need (why) not to use __traceback__ .

However, the Python 3 documentation for raise says that:

A trace object is usually created automatically when an exception occurs and attaches to it as the __traceback__ attribute, which is writable.

Therefore, I assume that it is intended to be used.

+59
Jan 28 '13 at 2:31 on
source share

There is a very good reason that the trace is not saved in the exception; because the trace contains references to its stack locators, this will cause a circular reference and (temporary) memory leak until it starts a GC circular (that's why you should never save the trace in a local variable .)

The only thing I can think of would be for you to monkeypatch stuff globals, so when he thinks he catches Exception , he actually catches a specialized type, and the exception applies to you as the caller:

 module_containing_stuff.Exception = type("BogusException", (Exception,), {}) try: stuff() except Exception: import sys print sys.exc_info() 
+8
Jul 10 '12 at 16:00
source share



All Articles