Skip to main content

29.5. warnings —警告控制

源代码: Lib/warnings.py


警告消息通常在以下情况下发出,其中警告用户在程序中的某些条件是有用的,其中该条件(通常)不需要提出异常并终止程序。例如,当程序使用过时的模块时,可能需要发出警告。

Python程序员通过调用此模块中定义的 warn() 函数来发出警告。 (C程序员使用 PyErr_WarnEx();有关详细信息,请参阅 异常处理)。

警告消息通常写入 sys.stderr,但是它们的处置可以灵活地更改,从忽略所有警告到将其变为异常。警告的处理可以根据警告类别(见下文),警告消息的文本和发出警告消息的源位置而变化。对相同源位置的特定警告的重复通常被抑制。

在警告控制中有两个阶段:首先,每次发出警告时,确定是否应该发出消息;接下来,如果要发出消息,则使用用户可设置的钩来格式化和打印消息。

警告过滤器控制是否发出警告消息的确定,该警告过滤器是匹配规则和动作的序列。可以通过调用 filterwarnings() 将规则添加到过滤器,并通过调用 resetwarnings() 将其重置为默认状态。

警告消息的打印通过调用 showwarning() 来完成,其可以被覆盖;该函数的默认实现通过调用 formatwarning() 格式化消息,这也可以由自定义实现使用。

参见

logging.captureWarnings() 允许您使用标准日志记录基础结构处理所有警告。

29.5.1. 警告类别

有一些内置的异常表示警告类别。这种分类对于能够过滤掉警告组是有用的。当前定义了以下警告类别类:

描述

Warning

这是所有警告类别类的基类。它是 Exception 的子类。

UserWarning

warn() 的默认类别。

DeprecationWarning

用于关于已弃用功能的警告的基本类别(默认情况下已忽略)。

SyntaxWarning

用于关于可疑句法特征的警告的基本类别。

RuntimeWarning

用于有关可疑运行时功能的警告的基本类别。

FutureWarning

对于将来会在语义上更改的构造的警告的基本类别。

PendingDeprecationWarning

对于将来将被弃用的功能的警告的基本类别(默认情况下将被忽略)。

ImportWarning

导入模块过程中触发的警告的基本类别(默认情况下忽略)。

UnicodeWarning

与Unicode相关的警告的基本类别。

BytesWarning

bytesbytearray 相关的警告的基本类别。

ResourceWarning

与资源使用相关的警告的基本类别。

虽然这些是技术上内置的异常,它们在这里被记录,因为在概念上它们属于警告机制。

用户代码可以通过对一个标准警告类别进行子类化来定义其他警告类别。警告类别必须始终是 Warning 类的子类。

29.5.2. 警告过滤器

warnings过滤器控制是否忽略,显示或转换为错误(引发异常)。

在概念上,警告过滤器维护过滤器规范的有序列表;任何特定警告将依次与列表中的每个过滤器规范匹配,直到找到匹配为止;匹配确定匹配的处置。每个条目是形式(actionmessagecategorymodulelineno)的元组,其中:

  • action 是以下字符串之一:

    处置

    "error"

    将匹配警告转换为异常

    "ignore"

    从不打印匹配的警告

    "always"

    始终打印匹配的警告

    "default"

    为发出警告的每个位置打印匹配警告的第一次出现

    "module"

    为发出警告的每个模块打印第一次出现的匹配警告

    "once"

    仅打印匹配警告的第一次出现,而不考虑位置

  • message 是包含正则表达式的字符串,警告消息的开始必须匹配。表达式编译为始终不区分大小写。

  • category 是一个类(Warning 的子类),其警告类别必须是子类才能匹配。

  • module 是包含模块名称必须匹配的正则表达式的字符串。表达式编译为区分大小写。

  • lineno 是一个整数,警告发生的行号必须匹配,或 0 匹配所有行号。

由于 Warning 类是从内置的 Exception 类派生的,为了将警告转换为错误,我们只需要提高 category(message)

警告过滤器通过传递给Python解释器命令行的 -W 选项初始化。解释器在 sys.warnoptions 中保存所有 -W 选项的参数而不解释; warnings 模块在首次导入时解析这些(在将消息打印到 sys.stderr 之后,将忽略无效选项)。

29.5.2.1. 默认警告过滤器

默认情况下,Python安装了几个警告过滤器,可以通过传递给 -W 和调用 filterwarnings() 的命令行选项来覆盖它们。

在 3.2 版更改: PendingDeprecationWarning 之外,默认情况下将忽略 DeprecationWarning

29.5.3. 暂时抑制警告

如果您使用的代码,您知道会引发一个警告,如一个已弃用的功能,但不想看到警告,那么可以使用 catch_warnings 上下文管理器:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn()

在上下文管理器中,所有警告都将被忽略。这允许您使用已知已弃用的代码,而不必看到警告,而不抑制可能不知道其使用已弃用代码的其他代码的警告。注意:这只能在单线程应用程序中保证。如果两个或多个线程同时使用 catch_warnings 上下文管理器,那么行为是未定义的。

29.5.4. 测试警告

要测试代码引发的警告,请使用 catch_warnings 上下文管理器。使用它,您可以临时改变警告过滤器,以方便您的测试。例如,执行以下操作捕获所有要检查的提高的警告:

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)

还可以使用 error 而不是 always 使所有警告成为异常。需要注意的一点是,如果由于 once/default 规则已经出现警告,则无论设置了什么过滤器,警告都不会再次出现,除非与警告相关的警告注册表已被清除。

一旦上下文管理器退出,警告过滤器将恢复为其在输入上下文时的状态。这可防止测试在测试之间以意想不到的方式更改警告过滤器,并导致不确定的测试结果。模块中的 showwarning() 功能也恢复为其原始值。注意:这只能在单线程应用程序中保证。如果两个或多个线程同时使用 catch_warnings 上下文管理器,则行为是未定义的。

当测试多个提出相同类型警告的操作时,重要的是以确认每个操作都提出新警告的方式来测试它们(例如设置警告作为异常提出,并检查操作引发异常,检查长度的警告列表在每次操作后继续增加,或者在每个新操作之前从警告列表中删除先前的条目)。

29.5.5. 更新Python的新版本的代码

默认情况下会忽略开发人员感兴趣的警告。因此,您应该确保测试您的代码,通常忽略警告可见。你可以从命令行通过传递 -Wd 到解释器(这是 -W default 的速记)。这将为所有警告启用默认处理,包括默认情况下忽略的警告。要更改遇到的警告所采取的操作,只需更改传递给 -W 的参数即可。 -W error。有关可能的更多详细信息,请参阅 -W 标志。

要以编程方式做同样的 -Wd,使用:

warnings.simplefilter('default')

请务必尽快执行此代码。这防止了登记哪些警告已经引起意外地影响未来警告的处理方式。

默认情况下忽略某些警告是为了防止用户看到只对开发者感兴趣的警告。由于您不一定能够控制用户使用哪种解释器来运行代码,因此可能会在您的发布周期之间发布新版本的Python。新的解释器版本可能会在您的代码中触发新的警告,这在旧的解释器中不存在。 DeprecationWarning 用于您正在使用的模块。虽然您作为开发人员希望通知您的代码正在使用已弃用的模块,但对用户而言,此信息本质上是噪音,对他们没有任何好处。

unittest 模块也已更新,以便在运行测试时使用 'default' 过滤器。

29.5.6. 可用功能

warnings.warn(message, category=None, stacklevel=1, source=None)

发出警告,或者可能忽略它或引发异常。 category 参数,如果给出,必须是一个警告类别类(见上文);它默认为 UserWarning。或者,message 可以是 Warning 实例,在这种情况下,将忽略 category 并使用 message.__class__。在这种情况下,消息文本将是 str(message)。如果发出的特定警告由警告过滤器更改为错误,则此函数引发异常。 stacklevel 参数可以由Python编写的封装函数使用,像这样:

def deprecation(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)

这使得警告引用 deprecation() 的调用者,而不是 deprecation() 本身的源(因为后者会失去警告消息的目的)。

source (如果提供)是发射 ResourceWarning 的被破坏对象。

在 3.6 版更改: 添加了 source 参数。

warnings.warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None)

这是 warn() 功能的低级接口,明确传递消息,类别,文件名和行号,以及可选的模块名称和注册表(应该是模块的 __warningregistry__ 字典)。模块名称默认为带有 .py 的文件名;如果没有传递注册表,则不会抑制警告。 message 必须是字符串,categoryWarningmessage 的子类可以是 Warning 实例,在这种情况下 category 将被忽略。

module_globals (如果提供)应该是发出警告的代码正在使用的全局命名空间。 (此参数用于支持在zipfiles或其他非文件系统导入源中找到的模块的源代码)。

source (如果提供)是发射 ResourceWarning 的被破坏对象。

在 3.6 版更改: 添加 source 参数。

warnings.showwarning(message, category, filename, lineno, file=None, line=None)

向文件写入警告。默认实现调用 formatwarning(message, category, filename, lineno, line) 并将结果字符串写入 file,默认为 sys.stderr。您可以通过分配给 warnings.showwarning 来替换任何可调用的函数。 line 是包含在警告消息中的一行源代码;如果未提供 line,则 showwarning() 将尝试读取由 filenamelineno 指定的行。

warnings.formatwarning(message, category, filename, lineno, line=None)

按标准方式格式化警告。这将返回一个字符串,它可能包含嵌入的换行符,并以换行符结束。 line 是包含在警告消息中的一行源代码;如果不提供 lineformatwarning() 将尝试读取由 filenamelineno 指定的行。

warnings.filterwarnings(action, message='', category=Warning, module='', lineno=0, append=False)

警告过滤器规格 列表中插入一个条目。默认情况下,条目插入在前面;如果 append 为真,则在末尾插入。它检查参数的类型,编译 messagemodule 正则表达式,并将它们作为警告过滤器列表中的元组插入。更靠近列表前面的条目会覆盖列表中后面的条目,如果两者都匹配特定的警告。省略的参数默认为匹配一切的值。

warnings.simplefilter(action, category=Warning, lineno=0, append=False)

警告过滤器规格 列表中插入一个简单的条目。函数参数的含义与 filterwarnings() 一样,但是不需要正则表达式,因为插入的过滤器总是匹配任何模块中的任何消息,只要类别和行号匹配。

warnings.resetwarnings()

重置警告过滤器。这会丢弃所有以前对 filterwarnings() 的调用的影响,包括 -W 命令行选项和对 simplefilter() 的调用的影响。

29.5.7. 可用的上下文管理器

class warnings.catch_warnings(*, record=False, module=None)

上下文管理器复制,退出时恢复警告过滤器和 showwarning() 功能。如果 record 参数是 False (缺省值),则上下文管理器在入口处返回 None。如果 recordTrue,则返回一个列表,该列表逐步填充有由定制 showwarning() 函数(也抑制对 sys.stdout 的输出)所见的对象。列表中的每个对象都具有与 showwarning() 的参数具有相同名称的属性。

module 参数采用将使用的模块,而不是在您导入其过滤器将受保护的 warnings 时返回的模块。这个参数主要用于测试 warnings 模块本身。

注解

catch_warnings 管理器通过替换然后稍后恢复模块的 showwarning() 功能和过滤器规范的内部列表来工作。这意味着上下文管理器正在修改全局状态,因此不是线程安全的。