See the traceback module, specifically the format_exc() function. Here.
import traceback
try:
raise ValueError
except ValueError:
tb = traceback.format_exc()
else:
tb = "No error"
finally:
print(tb)
Answer from kindall on Stack OverflowSee the traceback module, specifically the format_exc() function. Here.
import traceback
try:
raise ValueError
except ValueError:
tb = traceback.format_exc()
else:
tb = "No error"
finally:
print(tb)
Let's create a decently complicated stacktrace, in order to demonstrate that we get the full stacktrace:
def raise_error():
raise RuntimeError('something bad happened!')
def do_something_that_might_error():
raise_error()
Logging the full stacktrace
A best practice is to have a logger set up for your module. It will know the name of the module and be able to change levels (among other attributes, such as handlers)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
And we can use this logger to get the error:
try:
do_something_that_might_error()
except Exception as error:
logger.exception(error)
Which logs:
ERROR:__main__:something bad happened!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
And so we get the same output as when we have an error:
>>> do_something_that_might_error()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Getting just the string
If you really just want the string, use the traceback.format_exc function instead, demonstrating logging the string here:
import traceback
try:
do_something_that_might_error()
except Exception as error:
just_the_string = traceback.format_exc()
logger.debug(just_the_string)
Which logs:
DEBUG:__main__:Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
The correct function for tb.something(e) is
''.join(tb.format_exception(None, e, e.__traceback__))
This approach of this answer is new since Python 3.10. This reusable function writes the exception and traceback into a string buffer, and from there as a string.
import io
import traceback
def get_exception_traceback_str(exc: Exception) -> str:
# Ref: https://stackoverflow.com/a/76584117/
file = io.StringIO()
traceback.print_exception(exc, file=file)
return file.getvalue().rstrip()
Sample usage:
try:
assert False, 'testing assertion'
except Exception as exc:
error = get_exception_traceback_str(exc)
print(error)
Sample output:
Traceback (most recent call last):
File "<input>", line 2, in <module>
AssertionError: testing assertion
Note: To just print the exception and traceback instead, you don't need it in a string, so consider using just traceback.print_exception(exc).
The answer to this question depends on the version of Python you're using.
In Python 3
It's simple: exceptions come equipped with a __traceback__ attribute that contains the traceback. This attribute is also writable, and can be conveniently set using the with_traceback method of exceptions:
raise Exception("foo occurred").with_traceback(tracebackobj)
These features are minimally described as part of the raise documentation.
All credit for this part of the answer should go to Vyctor, who first posted this information. I'm including it here only because this answer is stuck at the top, and Python 3 is becoming more common.
In Python 2
It's annoyingly complex. The trouble with tracebacks is that they have references to stack frames, and stack frames have references to the tracebacks that have references to stack frames that have references to... you get the idea. This causes problems for the garbage collector. (Thanks to ecatmur for first pointing this out.)
The nice way of solving this would be to surgically break the cycle after leaving the except clause, which is what Python 3 does. The Python 2 solution is much uglier: you are provided with an ad-hoc function,sys.exc_info(), which only works inside the except clause. It returns a tuple containing the exception, the exception type, and the traceback for whatever exception is currently being handled.
So if you are inside the except clause, you can use the output of sys.exc_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 edit indicates, you're trying to get the traceback that would have been printed if your exception had not been handled, after it has already been handled. That's a much harder question. Unfortunately, sys.exc_info returns (None, None, None) when no exception is being handled. Other related sys attributes don't help either. sys.exc_traceback is deprecated and undefined when no exception is being handled; sys.last_traceback seems perfect, but it appears only to be defined during interactive sessions.
If you can control how the exception is raised, you might be able to use inspect and a custom exception to store some of the information. But I'm not entirely sure how that would work.
To tell the truth, catching and returning an exception is kind of an unusual thing to do. This might be a sign that you need to refactor anyway.
Since Python 3.0[PEP 3109] the built in class Exception has a __traceback__ attribute which contains a traceback object (with 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 few articles but none of them describes whether or why you should (not) use __traceback__.
However, the Python 3 documentation for raise says that:
A traceback object is normally created automatically when an exception is raised and attached to it as the
__traceback__attribute, which is writable.
So I assume it's meant to be used.