警告-警告控制

源代码: Lib/warnings.py


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

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

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

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

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

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

See also

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

Warning Categories

有许多内置的异常表示警告类别。这种分类对于能够过滤出警告组很有用。

尽管从技术上讲它们是built-in exceptions,但在此处进行了记录,因为从概念上讲它们属于警告机制。

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

当前定义了以下警告类别类:

ClassDescription
Warning这是所有警告类别类的 Base Class。它是Exception的子类。
UserWarningwarn()的默认类别。
DeprecationWarning当警告已针对其他 Python 开发人员使用时,有关警告的基本类别(默认情况下将被忽略,除非由__main__中的代码触发)。
SyntaxWarning警告有关可疑语法Function的基本类别。
RuntimeWarning有关可疑的运行时Function的警告的基本类别。
FutureWarning当这些警告旨在供使用 Python 编写的应用程序的finally用户使用时,这些警告的基本类别。
PendingDeprecationWarning有关将来将不推荐使用的Function的警告的基本类别(默认情况下被忽略)。
ImportWarning导入模块过程中触发的警告的基本类别(默认情况下忽略)。
UnicodeWarning与 Unicode 相关的警告的基本类别。
BytesWarningbytesbytearray相关的警告的基本类别。
ResourceWarning与资源使用相关的警告的基本类别。

在 3.7 版中进行了更改:以前是根据Function是完全删除还是更改其行为来区分DeprecationWarningFutureWarning。现在,根据目标受众和默认警告过滤器对它们的处理方式来区分它们。

警告过滤器

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

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

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

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

如果报告了警告,但与任何已注册的过滤器都不匹配,则将应用“默认”操作(因此其名称)。

描述警告过滤器

警告过滤器由传递给 Python 解释器命令行的-W选项和 PYTHONWARNINGS环境变量初始化。解释器将所有提供的条目的参数保存,而无需在sys.warnoptions中进行解释; warnings模块在首次导入时进行解析(将消息打印到sys.stderr之后,将忽略无效的选项)。

各个警告过滤器指定为由冒号分隔的一系列字段:

action:message:category:module:line

每个字段的含义如警告过滤器中所述。当在同一行上列出多个过滤器时(如 PYTHONWARNINGS),各个过滤器之间用逗号隔开,后面列出的过滤器优先于在其之前列出的过滤器(因为它们是从左到右应用的,也是最新的)应用的过滤器优先于较早的过滤器)。

常用的警告过滤器适用于所有警告,特定类别的警告或特定模块或程序包引发的警告。一些例子:

default                      # Show all warnings (even those ignored by default)
ignore                       # Ignore all warnings
error                        # Convert all warnings to errors
error::ResourceWarning       # Treat ResourceWarning messages as errors
default::DeprecationWarning  # Show DeprecationWarning messages
ignore,default:::mymodule    # Only report warnings triggered by "mymodule"
error:::mymodule[.*]         # Convert warnings to errors in "mymodule"
                             # and any subpackages of "mymodule"

默认警告过滤器

默认情况下,Python 安装几个警告过滤器,可以被-W命令行选项, PYTHONWARNINGS环境变量和对filterwarnings()的调用覆盖。

在常规版本中,默认警告过滤器具有以下条目(按优先 Sequences 排列):

default::DeprecationWarning:__main__
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::ResourceWarning

在调试版本中,默认警告过滤器列表为空。

在 3.2 版中进行了更改:DeprecationWarning现在默认被忽略,除了PendingDeprecationWarning

在 3.7 版中进行了更改:默认情况下,由__main__中的代码直接触发时,默认情况下会再次显示DeprecationWarning

在版本 3.7 中更改:BytesWarning不再出现在默认过滤器列表中,而是在两次指定-b时passsys.warnoptions配置。

覆盖默认过滤器

使用 Python 编写的应用程序的开发人员可能希望默认情况下对用户隐藏全部的 Python 级别警告,并且仅在运行测试或在应用程序上运行时显示它们。用来将过滤器配置传递给解释器的sys.warnoptions属性可用作标记,指示是否应禁用警告:

import sys

if not sys.warnoptions:
    import warnings
    warnings.simplefilter("ignore")

建议使用 Python 代码的测试运行者的开发人员改为确保默认情况下使用以下代码显示* all *警告用于被测代码:

import sys

if not sys.warnoptions:
    import os, warnings
    warnings.simplefilter("default") # Change the filter in this process
    os.environ["PYTHONWARNINGS"] = "default" # Also affect subprocesses

最后,建议使用以下代码(在其中user_ns是用于执行以交互方式 Importing 的代码的模块)的情况下,在非__main__的命名空间中运行用户代码的交互式 shell 的开发人员确保默认情况下使DeprecationWarning消息可见。

import warnings
warnings.filterwarnings("default", category=DeprecationWarning,
                                   module=user_ns.get("__name__"))

暂时禁止警告

如果您正在使用知道会发出警告的代码(例如已弃用的函数),但又不想看到该警告(即使已pass命令行明确配置了警告),则可以使用来禁止显示该警告。 catch_warnings上下文 Management 器:

import warnings

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

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

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

Testing Warnings

要测试代码引发的警告,请使用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 增加,或者在每次新操作之前,从警告列表中删除以前的条目)。

为依赖关系的新版本更新代码

默认情况下,Python 开发人员(而不是用 Python 编写的应用程序的finally用户)主要关注的警告类别将被忽略。

值得注意的是,此“默认情况下被忽略”列表包括DeprecationWarning(除__main__以外的每个模块),这意味着开发人员应确保使用可见的通常被忽略的警告来测试其代码,以便及时收到有关 FutureAPI 重大更改的通知(无论是否在标准库或第三方软件包)。

在理想情况下,代码将具有合适的测试套件,并且测试运行器将在运行测试时隐式启用所有警告(由unittest模块提供的测试运行器执行此操作)。

在不太理想的情况下,可以pass将-Wd传递给 Python 解释器(这是-W default的简写)或在环境中设置PYTHONWARNINGS=default来检查应用程序是否已弃用接口。这将启用所有警告的默认处理,包括默认情况下会忽略的警告。要更改针对遇到的警告采取的措施,您可以更改传递给-W的参数(例如-W error)。有关可能的更多详细信息,请参见-W标志。

Available Functions

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

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

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

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

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

在 3.6 版中进行了更改:添加* source *参数。

  • warnings. showwarning(消息类别文件名,* lineno file = None line = None *)

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

    • 以标准方式格式化警告。这将返回一个字符串,该字符串可能包含嵌入的换行符,并以换行符结尾。 * line 是警告消息中包含的一行源代码;如果未提供 line ,则formatwarning()将try读取 filename lineno *指定的行。
  • warnings. filterwarnings(* action message ='' category = Warning module ='' lineno = 0 append = False *)

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

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

可用的上下文 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 器正在修改全局状态,因此不是线程安全的。