获取警告的追踪信息

110 投票
5 回答
28699 浏览
提问于 2025-04-17 22:05

在numpy中,我们可以使用 np.seterr(invalid='raise') 这个命令,让警告变成错误,这样就能看到具体的错误信息和出错的位置(可以参考 这篇文章)。

  • 有没有什么通用的方法可以追踪警告信息呢?
  • 我能不能让python在出现警告时也给出错误的详细信息?

5 个回答

1

记录警告的选项,尽量不打断程序的运行。
获取错误追踪信息,但不抛出错误:

import warnings
import traceback

warnings.filterwarnings("error")  # Treat warnings as errors
try:
    your_code()
except Warning:
    print(traceback.format_exc())  # print traceback
warnings.resetwarnings()  # Back to default behavior

如果你想无论如何都执行相同(或类似)的代码,可以把它放在except块里,这次可以忽略警告,因为你已经得到了它的追踪信息:

import warnings
import traceback

warnings.filterwarnings("error")  # Treat warnings as errors
try:
    your_code()
except Warning:
    print(traceback.format_exc())  # print traceback
    warnings.filterwarnings("ignore")  # ignore warnings
    your_code()  # Execute either way (same code or alternative code)
warnings.resetwarnings()  # Back to default behavior
2

对于Python3,可以查看warnings模块文档中的stacklevel参数。当第三方库的警告信息被隐藏在调用堆栈中时,这个参数特别有用:你可以把调用warnings.warn时的参数设置为stacklevel=2,这样就能看到错误追踪信息,根据需要进行修改,然后再把stacklevel恢复到原来的状态。

例如:

warnings.warn("It's dangerous to go alone! Take this.", stacklevel=2)
6

你可以使用 warnings.filterwarnings() 这个功能,把特定的警告变成错误,这样你就能看到详细的错误信息和调用栈。下面是一个简单的示例:

import warnings
warnings.filterwarnings(
    action='error', message='',
    category=RuntimeWarning
)

在我的情况下:

import warnings
warnings.filterwarnings(
    'error', 'DateTimeField .* received a naive datetime',
    RuntimeWarning, 'django.db.models.fields'
)
50

像这样运行你的程序

python -W error myprogram.py

这样做会把所有的警告都变成致命错误,想了解更多信息可以查看这里

146

你可以通过给 warnings.showwarning 赋值来实现你想要的效果。实际上,警告模块的文档 也建议你这么做,所以这并不是走上了什么“黑暗的源代码”之路。 :)

你可以通过给 warnings.showwarning 赋值来替换这个函数,使用一个不同的实现。

你可以定义一个新的函数,它的功能和 warnings.showwarning 一样,但还会打印出调用栈。然后把这个新函数放到原来的位置:

import traceback
import warnings
import sys

def warn_with_traceback(message, category, filename, lineno, file=None, line=None):

    log = file if hasattr(file,'write') else sys.stderr
    traceback.print_stack(file=log)
    log.write(warnings.formatwarning(message, category, filename, lineno, line))

warnings.showwarning = warn_with_traceback

这样一来,每次出现警告时,都会打印出调用栈和警告信息。不过要注意,如果这个警告被忽略了(因为它不是第一次出现),那么就不会有任何反应,所以你仍然需要执行:

warnings.simplefilter("always")

你可以通过 warnings 模块的过滤器获得类似于 numpy.seterr 的控制效果。

如果你希望 Python 每次触发警告时都报告,而不仅仅是第一次,可以加入类似这样的代码:

import warnings
warnings.simplefilter("always")

你可以通过传递不同的字符串作为参数来获得其他行为。使用同一个函数,你还可以根据引发警告的模块、提供的消息、警告的类别、导致警告的代码行等等,指定不同的行为。

你可以在 模块文档 中查看相关列表。

举个例子,你可以设置所有的警告都抛出异常,除了 DeprecationWarnings,这些应该完全忽略:

import warnings
warnings.simplefilter("error")
warnings.simplefilter("ignore", DeprecationWarning)

这样你就能在每次抛出警告时获得完整的错误追踪(只有第一次会停止执行... 但你可以逐个处理这些警告,并创建一个过滤器来忽略你不想再听到的警告...)

撰写回答