28.6. 警告-警告控制

2.1 版中的新Function。

源代码: Lib/warnings.py


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

Python 程序员pass调用此模块中定义的warn()函数发出警告。 (C 程序员使用PyErr_WarnEx();有关详细信息,请参见Exception Handling)。

通常将警告消息写入sys.stderr,但是可以灵活地更改警告消息的处理方式,从忽略所有警告到将其变为异常。根据警告类别(请参阅下文),警告消息的文本以及发出警告的源位置,警告的处理方式可能会有所不同。通常会禁止针对同一源位置重复发出特定警告。

警告控制分为两个阶段:首先,每次发出警告时,都会确定是否应发布消息;接下来,如果要发布消息,则使用用户可设置的钩子对消息进行格式化和打印。

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

警告消息的打印是pass调用showwarning()来完成的,它可以被覆盖;此函数的默认实现pass调用formatwarning()来格式化消息,该信息也可用于自定义实现。

See also

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

28.6.1. 警告类别

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

ClassDescription
Warning这是所有警告类别类的 Base Class。它是Exception的子类。
UserWarningwarn()的默认类别。
DeprecationWarning有关不推荐使用的Function的警告的基本类别(默认情况下被忽略)。
SyntaxWarning警告有关可疑语法Function的基本类别。
RuntimeWarning有关可疑的运行时Function的警告的基本类别。
FutureWarning用于警告将来会在语义上更改的结构的警告的基本类别。
PendingDeprecationWarning有关将来将不推荐使用的Function的警告的基本类别(默认情况下被忽略)。
ImportWarning导入模块过程中触发的警告的基本类别(默认情况下忽略)。
UnicodeWarning与 Unicode 相关的警告的基本类别。
BytesWarning与字节和字节数组有关的警告的基本类别。

尽管这些是技术上内置的异常,但在此处进行了记录,因为从概念上讲,它们属于警告机制。

用户代码可以pass将标准警告类别之一子类化来定义其他警告类别。警告类别必须始终是Warning类的子类。

在 2.7 版中进行了更改:默认情况下,DeprecationWarning被忽略。

28.6.2. 警告过滤器

警告过滤器控制警告是被忽略,显示还是变成错误(引发异常)。

从概念上讲,警告过滤器会维护过滤器规格的有序列表。依次将所有特定警告与列表中的每个过滤器规范进行匹配,直到找到匹配项为止;match 决定 match 的安排。每个条目都是以下形式的 Tuples(* action message category module lineno *),其中:

    • action *是以下字符串之一:
ValueDisposition
"error"将匹配的警告变为异常
"ignore"从不打印匹配的警告
"always"始终打印匹配的警告
"default"在发出警告的每个位置打印匹配警告的第一次出现
"module"为发出警告的每个模块打印第一次出现的匹配警告
"once"无论位置如何,仅打印第一次出现的匹配警告
    • message *是一个字符串,其中包含警告消息的开头必须匹配的正则表达式。表达式被编译为始终不区分大小写。
    • category *是一个类(Warning的子类),警告类别必须是该子类才能匹配。
    • module *是一个字符串,其中包含模块名称必须匹配的正则表达式。该表达式被编译为区分大小写。
    • lineno *是一个整数,警告发生的行号必须匹配,或者0以匹配所有行号。

由于Warning类是从内置的Exception类派生的,因此要将警告变为错误,我们只需提高category(message)即可。

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

28.6.2.1. 默认警告过滤器

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

28.6.3. 暂时禁止警告

如果您使用的代码知道会发出警告(例如已弃用的函数),但又不想看到该警告,则可以使用catch_warnings上下文 Management 器来抑制该警告:

import warnings

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

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

在上下文 Management 器中,所有警告都将被忽略。这使您可以使用已知已弃用的代码,而不必查看警告,而不必为可能不知道其已弃用代码的其他代码取消警告。注意:只能在单线程应用程序中保证这一点。如果两个或多个线程同时使用catch_warnings上下文 Management 器,则该行为是不确定的。

28.6.4. 测试警告

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

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)

pass使用error而不是always,也可以使所有警告均为 exception。要注意的一件事是,如果由于once/default规则而已经发出警告,那么,除非设置了与该警告相关的警告注册表,否则无论设置了什么过滤器,都不会再次看到该警告。

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

当测试引发相同警告的多个操作时,以确认每个操作正在发出新警告的方式进行测试非常重要(例如,将警告设置为引发异常,并检查操作引发异常,并检查操作长度)每次操作后,警告列表的数量 continue 增加,或者在每次新操作之前,从警告列表中删除以前的条目)。

28.6.5. 为新版本的 Python 更新代码

默认情况下,仅会忽略开发人员感兴趣的警告。因此,您应该确保使用可见的通常被忽略的警告来测试代码。您可以pass在命令行中将-Wd传递给解释器来完成此操作(这是-W default的简写)。这将启用所有警告的默认处理,包括默认情况下会忽略的警告。要更改针对遇到的警告采取的措施,您只需更改将传递给-W的参数,例如-W error。有关可能的更多详细信息,请参见-W标志。

要pass编程方式与-Wd相同,请使用:

warnings.simplefilter('default')

确保尽快执行此代码。这样可以防止已发出警告的记录意外地影响将来的警告的处理方式。

默认情况下,某些警告会被忽略,以防止用户看到开发人员仅感兴趣的警告。由于您不一定控制用户使用什么解释器来运行其代码,因此有可能在您的发布周期之间发布新版本的 Python。新的解释器版本可能会在您的代码中触发旧的解释器中不存在的新警告,例如DeprecationWarning用于您正在使用的模块。当您作为开发人员希望收到有关您的代码正在使用不推荐使用的模块的通知时,对于用户而言,此信息本质上是噪音,对他们没有任何好处。

28.6.6. 可用Function

  • warnings. warn(* message * [,* category * [,* stacklevel *]])
    • 发出警告,或者忽略它或引发异常。 * category 参数(如果给定)必须是警告类别类(请参见上文);默认为UserWarning。另外, message 可以是Warning实例,在这种情况下, category 将被忽略,而将使用message.__class__。在这种情况下,消息文本将为str(message)。如果发出的特定警告被警告过滤器更改为错误,则此函数引发异常。 Python 的包装函数可以使用 stacklevel *参数,如下所示:
def deprecation(message):
    warnings.warn(message, DeprecationWarning, stacklevel=2)

这使警告指向deprecation()的调用者,而不是deprecation()本身的来源(因为后者会破坏警告消息的目的)。

  • warnings. warn_explicit(* message category filename lineno * [,* module * [,* registry * [,* module_globals *]]])
    • 这是warn()Function的低级接口,显式传递消息,类别,文件名和行号,以及可选的模块名称和注册表(应为模块的__warningregistry__字典)。模块名称默认为带.py剥离的文件名;如果没有pass注册表,则永远不会取消该警告。 * message 必须是字符串, category * Warning或* message 的子类可能是Warning实例,在这种情况下, category *将被忽略。
  • module_globals *(如果提供)应该是发出警告的代码正在使用的全局名称空间。 (此参数用于支持显示在 zipfile 或其他非文件系统导入源中找到的模块的源)。

在版本 2.5 中进行了更改:添加了* module_globals *参数。

  • warnings. warnpy3k(* message * [,* category * [,* stacklevel *]])
    • 发出与 Python 3.x 弃用相关的警告。仅在使用-3 选项启动 Python 时显示警告。像warn()一样,* message 必须是字符串,而 category *必须是Warning的子类。 warnpy3k()使用DeprecationWarning作为默认警告类别。

2.6 版的新Function。

  • warnings. showwarning(消息类别文件名,* lineno * [,* file * [,* line *]])
    • 向文件写警告。默认实现调用formatwarning(message, category, filename, lineno, line)并将结果字符串写入* file *,默认为sys.stderr。您可以pass分配给warnings.showwarning来将此函数替换为替代实现。 * line 是警告消息中包含的一行源代码;如果未提供 line ,则showwarning()将try读取 filename lineno *指定的行。

在 2.7 版中更改:必须支持* line *参数。

  • warnings. formatwarning(消息类别文件名,* lineno * [,* line *])
    • 以标准方式格式化警告。这将返回一个字符串,该字符串可能包含嵌入的换行符,并以换行符结尾。 * line 是警告消息中包含的一行源代码;如果未提供 line ,则formatwarning()将try读取 filename lineno *指定的行。

在 2.6 版中进行了更改:添加了* line *参数。

  • warnings. filterwarnings(* action * [,* message * [,* category * [,* module * [,* lineno * [,* append *]]]]]))

    • 警告过滤器规格列表中插入一个条目。默认情况下,该条目插入在最前面;如果* append 为 true,则将其插入到末尾。这将检查参数的类型,编译 message module *正则表达式,并将它们作为 Tuples 插入警告过滤器列表中。如果两个列表均与特定警告匹配,则更靠近列表开头的条目将覆盖列表后面的条目。Ellipsis的参数默认为匹配所有内容的值。
  • warnings. simplefilter(* action * [,* category * [,* lineno * [,* append *]]])

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

28.6.7. 可用的上下文 Management 器

    • class * warnings. catch_warnings([** record = False module = None *])
    • 上下文 Management 器可以复制并在退出时恢复警告过滤器和showwarning()函数。如果* record 参数为False(默认值),则上下文 Management 器在 Importing 时返回None。如果 record *是True,则返回一个列表,该列表将逐步包含对象,如自定义showwarning()函数所看到的那样(该函数还会抑制向sys.stdout的输出)。列表中的每个对象都具有与showwarning()的参数同名的属性。
  • module *参数采用的模块将代替导入warnings时其过滤器将受到保护的返回模块。该参数主要用于测试warnings模块本身。

Note

catch_warningsManagement 器的工作方式是替换并随后恢复模块的showwarning()Function和过滤器规格的内部列表。这意味着上下文 Management 器正在修改全局状态,因此不是线程安全的。

Note

在 Python 3 中,catch_warnings的构造函数参数是仅关键字参数。

2.6 版的新Function。