如何在不停止程序的情况下打印完整的回溯?

问题:如何在不停止程序的情况下打印完整的回溯?

我正在编写一个程序,该程序可以解析10个网站,找到数据文件,保存文件,然后解析它们以生成可以在NumPy库中轻松使用的数据。有万吨通过不良链接,不好的XML,缺项,其他的事情我还没有进行分类文件遇到错误的。我最初制作该程序来处理以下错误:

try:
    do_stuff()
except:
    pass

但是现在我想记录错误:

try:
    do_stuff()
except Exception, err:
    print Exception, err

请注意,这是打印到日志文件中以供以后查看。这通常会打印非常无用的数据。我想要的是在错误触发时打印完全相同的行,而没有try-except拦截异常,但是我不希望它暂停我的程序,因为它嵌套在我想要的一系列for循环中看到完成。

I’m writing a program that parses 10 websites, locates data files, saves the files, and then parses them to make data that can be readily used in the NumPy library. There are tons of errors this file encounters through bad links, poorly formed XML, missing entries, and other things I’ve yet to categorize. I initially made this program to handle errors like this:

try:
    do_stuff()
except:
    pass

But now I want to log errors:

try:
    do_stuff()
except Exception, err:
    print Exception, err

Note this is printing to a log file for later review. This usually prints very useless data. What I want is to print the exact same lines printed when the error triggers without the try-except intercepting the exception, but I don’t want it to halt my program since it is nested in a series of for loops that I would like to see to completion.


回答 0

其他一些答案已经指出了追溯模块。

请注意,使用print_exc,在某些特殊情况下,您将无法获得预期的结果。在Python 2.x中:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

…将显示最后一个异常的回溯:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

如果您确实需要访问原始的追溯,一种解决方案是将异常信息exc_info本地变量中返回,并使用来显示它print_exception

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

生产:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

很少有这个陷阱:

  • 从文档sys_info

    在处理异常的函数中将回溯返回值分配给局部变量将导致循环引用。这将防止垃圾回收由同一函数中的局部变量或回溯引用的任何内容。[…] 如果确实需要回溯,请确保在使用后将其删除(最好通过try … finally语句完成)

  • 但是,根据同一文档:

    从Python 2.2开始,启用垃圾收集并使其无法访问时,会自动回收此类循环,但是避免创建循环仍然更加有效。


另一方面,通过允许您访问异常关联的回溯,Python 3产生了一个不太令人惊讶的结果:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

…将显示:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")

Some other answer have already pointed out the traceback module.

Please notice that with print_exc, in some corner cases, you will not obtain what you would expect. In Python 2.x:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

…will display the traceback of the last exception:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

If you really need to access the original traceback one solution is to cache the exception infos as returned from exc_info in a local variable and display it using print_exception:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

Producing:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

Few pitfalls with this though:

  • From the doc of sys_info:

    Assigning the traceback return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced by a local variable in the same function or by the traceback from being garbage collected. […] If you do need the traceback, make sure to delete it after use (best done with a try … finally statement)

  • but, from the same doc:

    Beginning with Python 2.2, such cycles are automatically reclaimed when garbage collection is enabled and they become unreachable, but it remains more efficient to avoid creating cycles.


On the other hand, by allowing you to access the traceback associated with an exception, Python 3 produce a less surprising result:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

… will display:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")

回答 1

traceback.format_exc()sys.exc_info()会产生更多信息(如果您想要的话)。

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

traceback.format_exc() or sys.exc_info() will yield more info if that’s what you want.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

回答 2

如果您正在调试,并且只想查看当前的堆栈跟踪,则可以简单地调用:

traceback.print_stack()

无需为了再次捕获而手动引发异常。

If you’re debugging and just want to see the current stack trace, you can simply call:

traceback.print_stack()

There’s no need to manually raise an exception just to catch it again.


回答 3

如何在不停止程序的情况下打印完整的回溯?

当您不想因错误而暂停程序时,需要使用try / except处理该错误:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

要提取完整的追溯,我们将使用traceback标准库中的模块:

import traceback

并创建一个相当复杂的堆栈跟踪以演示我们获得了完整的堆栈跟踪:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

列印

打印完整的回溯,请使用以下traceback.print_exc方法:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

哪些打印:

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!

比打印,记录更好:

但是,最佳实践是为模块设置一个记录器。它将知道模块的名称,并能够更改级别(在其他属性中,例如处理程序)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

在这种情况下,您将需要该logger.exception函数:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

哪个日志:

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!

或者,也许您只想要字符串,在这种情况下,您将需要traceback.format_exc函数:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

哪个日志:

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!

结论

对于这三个选项,我们看到的输出与发生错误时的输出相同:

>>> 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!

How to print the full traceback without halting the program?

When you don’t want to halt your program on an error, you need to handle that error with a try/except:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

To extract the full traceback, we’ll use the traceback module from the standard library:

import traceback

And to create a decently complicated stacktrace to demonstrate that we get the full stacktrace:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Printing

To print the full traceback, use the traceback.print_exc method:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

Which prints:

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!

Better than printing, logging:

However, 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__)

In which case, you’ll want the logger.exception function instead:

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!

Or perhaps you just want the string, in which case, you’ll want the traceback.format_exc function instead:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

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!

Conclusion

And for all three options, we see 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!

回答 4

首先,不要用printS表示日志记录,有非稳态,证明和深思熟虑的STDLIB模块,这样做:logging。您绝对应该使用它。

其次,当存在本机且简单的方法时,不要试图将无关的工具弄得一团糟。这里是:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

而已。现在完成了。

对任何对引擎盖如何工作感兴趣的人的解释

什么log.exception是真正做只是为了通话log.error(即记录与级别的事件ERROR,并打印回溯然后。

为什么会更好?

好,这是一些注意事项:

  • 这是正确的 ;
  • 这很简单;
  • 很简单。

为什么没有人可以使用traceback记录仪exc_info=True或与其通话,或者弄脏记录仪sys.exc_info

好吧,只是因为!它们全都出于不同的目的而存在。例如,traceback.print_exc的输出与解释器本身产生的回溯有些不同。如果您使用它,则会使任何人阅读您的日志感到困惑,他们会撞到他们的头。

传递exc_info=True日志记录是不合适的。但是,当捕获可恢复的错误并且您还希望使用INFO回溯记录它们(使用例如级别)时,它很有用,因为它log.exception只会生成一个级别的日志- ERROR

而且,您绝对应该尽可能避免混乱sys.exc_info。它不是一个公共接口,而是一个内部接口- 如果您完全知道自己在做什么,就可以使用它。它不仅仅用于打印exceptions。

First, don’t use prints for logging, there is astable, proven and well-thought out stdlib module to do that: logging. You definitely should use it instead.

Second, don’t be tempted to do a mess with unrelated tools when there is native and simple approach. Here it is:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

That’s it. You are done now.

Explanation for anyone who is interested in how things work under the hood

What log.exception is actually doing is just a call to log.error (that is, log event with level ERROR) and print traceback then.

Why is it better?

Well, here is some considerations:

  • it is just right;
  • it is straightforward;
  • it is simple.

Why should nobody use traceback or call logger with exc_info=True or get their hands dirty with sys.exc_info?

Well, just because! They all exist for different purposes. For example, traceback.print_exc‘s output is a little bit different from tracebacks produced by the interpreter itself. If you use it, you will confuse anyone who reads your logs, they will be banging their heads against them.

Passing exc_info=True to log calls is just inappropriate. But, it is useful when catching recoverable errors and you want to log them (using, e.g INFO level) with tracebacks as well, because log.exception produces logs of only one level – ERROR.

And you definitely should avoid messing with sys.exc_info as much as you can. It’s just not a public interface, it’s an internal one – you can use it if you definitely know what you are doing. It is not intended for just printing exceptions.


回答 5

除了@Aaron Hall的答案外,如果您正在记录日志,但又不想使用logging.exception()(由于它记录为ERROR级别),则可以使用较低级别并通过exc_info=True。例如

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)

In addition to @Aaron Hall’s answer, if you are logging, but don’t want to use logging.exception() (since it logs at the ERROR level), you can use a lower level and pass exc_info=True. e.g.

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)

回答 6

为了得到精确的堆栈跟踪,作为一个字符串,已如果没有尝试/除非在那里步过它,只是把这个在除块捕获违规的异常上升。

desired_trace = traceback.format_exc(sys.exc_info())

这是使用方法(假设flaky_func已定义,并log调用了您喜欢的日志系统):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

捕获并重新引发KeyboardInterrupts 是一个好主意,这样您仍然可以使用Ctrl-C终止程序。日志记录不在问题范围之内,但是不错的选择是loggingsystraceback模块的文档。

To get the precise stack trace, as a string, that would have been raised if no try/except were there to step over it, simply place this in the except block that catches the offending exception.

desired_trace = traceback.format_exc(sys.exc_info())

Here’s how to use it (assuming flaky_func is defined, and log calls your favorite logging system):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

It’s a good idea to catch and re-raise KeyboardInterrupts, so that you can still kill the program using Ctrl-C. Logging is outside the scope of the question, but a good option is logging. Documentation for the sys and traceback modules.


回答 7

您需要将try / except放到可能发生错误的最内部循环中,即

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

… 等等

换句话说,您需要将可能在try / except中失败的语句包装在尽可能多的内部循环中,并尽可能不具体。

You will need to put the try/except inside the most innerloop where the error may occur, i.e.

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

… and so on

In other words, you will need to wrap statements that may fail in try/except as specific as possible, in the most inner-loop as possible.


回答 8

关于此答案的评论:print(traceback.format_exc())对我来说,做得更好traceback.print_exc()。对于后者,hello有时会奇怪地将其与回溯文本“混合”,例如,如果两者都想同时写入stdout或stderr,则会产生奇怪的输出(至少在文本编辑器内部进行构建并在“构建结果”面板)。

追溯(最近一次通话):
文件“ C:\ Users \ User \ Desktop \ test.py”,第7行,在
地狱 do_stuff()
文件“ C:\ Users \ User \ Desktop \ test.py”,第4行,在do_stuff
1/0
ZeroDivisionError中:整数除或以零为模的
o
[在0.1s内完成]

所以我用:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')

A remark about this answer‘s comments: print(traceback.format_exc()) does a better job for me than traceback.print_exc(). With the latter, the hello is sometimes strangely “mixed” with the traceback text, like if both want to write to stdout or stderr at the same time, producing weird output (at least when building from inside a text editor and viewing the output in the “Build results” panel).

Traceback (most recent call last):
File “C:\Users\User\Desktop\test.py”, line 7, in
hell do_stuff()
File “C:\Users\User\Desktop\test.py”, line 4, in do_stuff
1/0
ZeroDivisionError: integer division or modulo by zero
o
[Finished in 0.1s]

So I use:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')

回答 9

我在其他任何答案中都没有提到这一点。如果出于某种原因要传递Exception对象…

在Python 3.5+中,您可以使用traceback.TracebackException.from_exception()从Exception对象获取跟踪。例如:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

但是,以上代码导致:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

这只是堆栈的两个级别,与在引发异常stack_lvl_2()但未拦截异常的情况下在屏幕上显示的内容相反(取消注释# raise行)。

据我了解,这是因为stack_lvl_3()在这种情况下,异常在引发时仅记录堆栈的当前级别。随着它在堆栈中的传递,它被添加了更多的层次__traceback__。但是我们在中截获了它stack_lvl_2(),这意味着它要记录的只是3级和2级。要获得在stdout上打印的完整轨迹,我们必须在最高(最低?)级捕获它:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

结果是:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

注意,堆栈打印不同,缺少第一行和最后一行。因为是不同的format()

截取该异常离生成点越远越好,这使得代码更简单,同时也提供了更多信息。

I don’t see this mentioned in any of the other answers. If you’re passing around an Exception object for whatever reason…

In Python 3.5+ you can get a trace from an Exception object using traceback.TracebackException.from_exception(). For example:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

However, the above code results in:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

This is just two levels of the stack, as opposed to what would have been printed on screen had the exception been raised in stack_lvl_2() and not intercepted (uncomment the # raise line).

As I understand it, that’s because an exception records only the current level of the stack when it is raised, stack_lvl_3() in this case. As it’s passed back up through the stack, more levels are being added to its __traceback__. But we intercepted it in stack_lvl_2(), meaning all it got to record was levels 3 and 2. To get the full trace as printed on stdout we’d have to catch it at the highest (lowest?) level:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

Which results in:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

Notice that the stack print is different, the first and last lines are missing. Because it’s a different format().

Intercepting the exception as far away from the point where it was raised as possible makes for simpler code while also giving more information.


回答 10

您需要回溯模块。它可以让您像Python通常一样打印堆栈转储。特别是,print_last函数将打印最后的异常和堆栈跟踪。

You want the traceback module. It will let you print stack dumps like Python normally does. In particular, the print_last function will print the last exception and a stack trace.


回答 11

从异常对象以字符串形式获取完整的追溯 traceback.format_exception

如果只有异常对象,则可以使用以下命令从Python 3中的代码的任何点以字符串形式获取跟踪:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

完整示例:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

输出:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

文档:https : //docs.python.org/3.7/library/traceback.html#traceback.format_exception

另请参阅:从异常对象中提取回溯信息

在Python 3.7.3中测试。

Get the full traceback as a string from the exception object with traceback.format_exception

If you only have the exception object, you can get the traceback as a string from any point of the code in Python 3 with:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

Full example:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

Output:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

Documentation: https://docs.python.org/3.7/library/traceback.html#traceback.format_exception

See also: Extract traceback info from an exception object

Tested in Python 3.7.3.


回答 12

如果您已经有一个Error对象,并且想要打印整个内容,则需要进行以下稍微尴尬的调用:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

是的,print_exception需要三个位置参数:异常的类型,实际的异常对象以及异常自己的内部回溯属性。

在python 3.5或更高版本中,type(err)是可选的…但是它是一个位置参数,因此您仍然必须在其位置显式传递None。

traceback.print_exception(None, err, err.__traceback__)

我不知道为什么所有这些都不是唯一的traceback.print_exception(err)。为什么您要打印出错误以及与该错误无关的回溯,这超出了我的范围。

If you have an Error object already, and you want to print the whole thing, you need to make this slightly awkward call:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

That’s right, print_exception takes three positional arguments: The type of the exception, the actual exception object, and the exception’s own internal traceback property.

In python 3.5 or later, the type(err) is optional… but it’s a positional argument, so you still have to explicitly pass None in its place.

traceback.print_exception(None, err, err.__traceback__)

I have no idea why all of this isn’t just traceback.print_exception(err). Why you would ever want to print out an error, along with a traceback other than the one that belongs to that error, is beyond me.