# 29.5. `warnings` - 警告控制

源代码： Lib / warnings.py

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

Python程序员通过调用此模块中定义的`warn()`函数来发出警告。（C程序员使用`PyErr_WarnEx()`；有关详细信息，请参阅Exception Handling）。

警告消息通常写入`sys.stderr`，但它们的处置可以灵活更改，从忽略所有警告，将其转换为异常。警告的处理可以根据警告类别（见下文），警告消息的文本以及发出警告消息的源位置而变化。对于相同源位置的特定警告的重复通常被抑制。

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

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

警告消息的打印通过调用`showwarning()`完成，可能会被覆盖；此函数的默认实现通过调用`formatwarning()`格式化消息，这也可以由自定义实现使用。

也可以看看

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

## 29.5.1. Warning Categories

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

类 | 描述
--- | ---
`Warning` | 这是所有警告类别类的基类。它是`Exception`的子类。
`UserWarning` | `warn()`的默认类别。
`DeprecationWarning` | 用于关于已弃用功能的警告的基本类别（默认情况下已忽略）。
`SyntaxWarning` | 基本类别用于关于可疑句法特征的警告。
`RuntimeWarning` | 用于有关可疑运行时功能的警告的基本类别。
`FutureWarning` | 对于将来会在语义上更改的构造的警告的基本类别。
`PendingDeprecationWarning` | 对于将来将被弃用的功能的警告的基本类别（默认情况下将被忽略）。
`ImportWarning` | 导入模块过程中触发的警告的基本类别（默认情况下忽略）。
`UnicodeWarning` | 与Unicode相关的警告的基本类别。
`BytesWarning` | 与`bytes`和`bytearray`相关的警告的基本类别。
`ResourceWarning` | 与资源使用相关的警告的基本类别。

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

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

## 29.5.2. The Warnings Filter

warnings过滤器控制是否忽略，显示或转换为错误（引发异常）。

在概念上，警告过滤器维护过滤器规格的有序列表；任何特定的警告将依次与列表中的每个过滤器规范匹配，直到找到匹配为止；匹配确定匹配的处置。每个条目是形式（动作，消息，类别，模块，/ t4>），其中：

- action是以下字符串之一：

  值 | 处置
  --- | ---
  `"error"` | 将匹配警告转换为异常
  `"ignore"` | 从不打印匹配的警告
  `"always"` | 始终打印匹配的警告
  `"default"` | 为发出警告的每个位置打印匹配警告的第一次出现
  `"module"` | 为发出警告的每个模块打印第一次出现的匹配警告
  `"once"` | 只打印匹配警告的第一次出现，而不考虑位置

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

- category是一个类（`Warning`的子类），其中警告类别必须是子类才能匹配。

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

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

由于`Warning`类派生自内建`Exception`类，要将警告转为错误，我们只需引入`category(message)`。

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

### 29.5.2.1. Default Warning Filters

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

- `DeprecationWarning`和`PendingDeprecationWarning`和`ImportWarning`将被忽略。
- 除非`-b`选项被给定一次或两次； `BytesWarning`在这种情况下，将打印此警告（`-b`）或变成异常（`-bb`）。
- `ResourceWarning`被忽略，除非Python是在调试模式下构建的。

Changed in version 3.2: `DeprecationWarning` is now ignored by default in addition to `PendingDeprecationWarning`.

## 29.5.3. Temporarily Suppressing Warnings

如果您使用的代码已知会引发警告（例如已弃用的函数），但不希望看到该警告，则可以使用`catch_warnings`上下文管理器取消警告：

    import warnings

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

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

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

## 29.5.4. Testing Warnings

要测试代码引发的警告，请使用`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. Updating Code For New Versions of Python

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

要以编程方式执行与`-Wd`相同的操作，请使用：

    warnings.simplefilter('default')

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

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

在运行测试时，`unittest`模块也已更新为使用`'default'`过滤器。

## 29.5.6. Available Functions

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

  发出警告，或者可能忽略它或引发异常。类别参数（如果给出）必须是警告类别类（见上文）；它默认为`UserWarning`。或者，消息可以是`Warning`实例，在这种情况下，将忽略类别，将使用`message.__class__` 。在这种情况下，消息文本将是`str(message)`。如果发出的特定警告由警告过滤器更改为错误，则此函数引发异常。stacklevel参数可以用Python编写的wrapper函数使用，如下所示：

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

  这使得警告引用`deprecation()`的调用者，而不是`deprecation()`本身的源（因为后者将会违反警告消息的目的） 。

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

  这是对`warn()`功能的低级接口，显式传递消息，类别，文件名和行号，以及可选的模块名称和注册表（应为`__warningregistry__`模块的字典）。模块名称默认为带有`.py` stripped的文件名；如果没有传递注册表，则永远不会禁止警告。message must be a string and category a subclass of `Warning` or message may be a `Warning` instance, in which case category will be ignored.

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

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

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

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

  按标准方式格式化警告。这将返回一个字符串，它可能包含嵌入的换行符，并以换行符结束。行是要包括在警告消息中的一行源代码；如果未提供行，则`formatwarning()`将尝试读取由filename和lineno指定的行。

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

  在warnings filter specifications列表中插入一个条目。默认情况下，条目插入在前面；如果append为true，则在末尾插入。这会检查参数的类型，编译消息和模块正则表达式，并将其作为警告过滤器列表中的元组插入。更靠近列表前面的条目将覆盖列表中后面的条目（如果两者都匹配特定警告）。省略的参数默认为匹配一切的值。

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

  在warnings filter specifications列表中插入一个简单的条目。函数参数的含义是`filterwarnings()`，但是不需要正则表达式，因为插入的过滤器总是匹配任何模块中的任何消息，只要类别和行号匹配。

`warnings.``resetwarnings`()

  重置警告过滤器。这将舍弃之前对`filterwarnings()`的所有调用（包括`-W`命令行选项和对`simplefilter()`的调用）的影响。

## 29.5.7. Available Context Managers

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

  上下文管理器复制并退出时恢复警告过滤器和`showwarning()`函数。如果记录参数为`False`（默认值），则上下文管理器在条目上返回`None`。If record is `True`, a list is returned that is progressively populated with objects as seen by a custom `showwarning()` function (which also suppresses output to `sys.stdout`). 列表中的每个对象都具有与`showwarning()`的参数具有相同名称的属性。

  模块参数使用将使用的模块，而不是在导入其过滤器将受保护的`warnings`时返回的模块。此参数主要用于测试`warnings`模块本身。

  注意

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