Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 565 – 在 __main__ 中显示 DeprecationWarning

作者:
Alyssa Coghlan <ncoghlan at gmail.com>
状态:
最终
类型:
标准跟踪
创建:
2017年11月12日
Python 版本:
3.7
历史记录:
2017年11月12日,2017年11月25日
决议:
Python-Dev 消息

目录

摘要

在 Python 2.7 和 Python 3.2 中,默认警告过滤器已更新为默认隐藏 DeprecationWarning,以便用 Python 编写的开发工具(例如 linter、静态分析器、测试运行程序、代码生成器)以及任何其他碰巧是用 Python 编写的应用程序中的弃用警告,除非用户明确选择查看它们,否则不会显示给其用户。

但是,此更改产生了不幸的副作用,导致 DeprecationWarning 在其主要预期目的方面效率显著降低:向这些 API 的用户提供有关 API 中(无论是在 CPython、标准库还是在第三方库中)的重大更改的预先通知。

为了改善这种情况,本 PEP 提出对默认警告过滤器进行一项调整:默认显示归因于主模块的弃用警告。

此更改意味着在交互式提示符处输入的代码和单个文件脚本中的代码将恢复为默认报告这些警告,而对于作为可导入模块的一部分分发的打包代码,这些警告将继续默认被静默处理。

本 PEP 还建议对参考解释器和标准库文档进行一些小的调整,以帮助使警告子系统更容易被新的 Python 开发人员使用。

作为文档更新的一部分,将更清楚地说明 unittest 测试运行程序在执行测试用例时默认显示所有警告,并且建议其他测试运行程序遵循此示例。

规范

新的默认警告过滤器条目

当前的一组默认警告过滤器包括

ignore::DeprecationWarning
ignore::PendingDeprecationWarning
ignore::ImportWarning
ignore::BytesWarning
ignore::ResourceWarning

然后,默认的 unittest 测试运行程序使用 warnings.catch_warnings() warnings.simplefilter('default') 在运行测试用例时覆盖默认过滤器。

本 PEP 中提出的更改是将默认警告过滤器列表更新为

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

这意味着在警告的名义位置(由 warnings.warnstacklevel 参数确定)位于 __main__ 模块的情况下,每个 DeprecationWarning 的第一次出现将再次被报告。

此更改将导致 DeprecationWarning 默认显示

  • 在交互式提示符处直接执行的代码
  • 作为单个文件脚本的一部分直接执行的代码

同时继续默认隐藏

  • zipapp 档案的 __main__.py 文件中的另一个模块导入的代码
  • 从可执行包的 __main__ 子模块中的另一个模块导入的代码
  • 根据 console_scriptsgui_scripts 入口点定义在安装时生成的执行脚本包装器中导入的代码

这意味着创建可安装或可执行工件(例如 zipapp 档案)以分发给其用户的工具开发人员不应该看到与现状有任何变化,而更多临时个人或本地分发脚本的用户可能会再次开始看到相关的弃用警告(就像他们在 Python 2.6 及更早版本中一样)。

FutureWarning 的其他用例

标准库文档将更新为明确建议使用 FutureWarning(而不是 DeprecationWarning)来表示旨在供应用程序的用户看到的向后兼容性警告。(这将是除了现有的使用 FutureWarning 来警告将来将保持有效代码但具有不同语义的构造之外)。

这将给出以下三种不同的向后兼容性警告类别,以及三个不同的目标受众

  • PendingDeprecationWarning:默认情况下对所有代码隐藏。目标受众是积极关注确保其软件未来兼容性的 Python 开发人员(例如,具有特定支持义务的专业 Python 应用程序开发人员)。
  • DeprecationWarning:默认情况下报告在 __main__ 模块中直接运行的代码(因为此类代码被认为不太可能拥有专用的测试套件),但默认情况下对其他模块中的代码隐藏。目标受众是其软件可能因其依赖项升级(包括 Python 本身升级)而中断的 Python 开发人员(例如,使用 Python 编写脚本环境的开发人员,其中其他人控制着依赖项升级的时间)。
  • FutureWarning:默认情况下报告所有代码。目标受众是使用 Python 编写的应用程序的用户,而不是其他 Python 开发人员(例如,警告关于在配置文件格式中使用弃用设置)。

对于希望确保其 API 兼容性警告更可靠地被其用户看到的库和框架作者,建议在 Python 3.7 及更高版本中使用从 DeprecationWarning 派生的自定义警告类,在早期版本中使用从 FutureWarning 派生的自定义警告类。

其他文档更新

当前的警告系统参考文档在 -W 命令行选项或 PYTHONWARNINGS 环境变量的可能设置的具体示例方面相对较少,这些设置可以实现特定的最终结果。

以下改进建议作为本 PEP 的实现的一部分

  • PYTHONWARNINGS 环境变量的描述下明确列出以下条目
    PYTHONWARNINGS=error # Convert to exceptions
    PYTHONWARNINGS=always # Warn every time
    PYTHONWARNINGS=default # Warn once per call location
    PYTHONWARNINGS=module # Warn once per calling module
    PYTHONWARNINGS=once # Warn once per Python process
    PYTHONWARNINGS=ignore # Never warn
    
  • -W 命令行开关文档下列出的每个警告操作明确列出相应的简写选项(-We-Wa-Wd-Wm-Wo-Wi
  • warnings 模块文档中明确列出默认过滤器集,使用 action::categoryaction::category:module 表示法
  • warnings.simplefilter 文档中明确列出以下代码段,作为在 Python 应用程序中默认关闭所有警告同时仍然允许通过 PYTHONWARNINGS-W 命令行开关将其重新打开的推荐方法
    if not sys.warnoptions:
        warnings.simplefilter("ignore")
    

这些都不是新的(它们在所有仍在支持的 Python 版本中都已有效),但鉴于相关文档的当前结构,它们并不特别明显。

参考实现

参考实现可在与本 PEP 相关的跟踪问题[5]链接的 PR [4]中找到。

作为实现本 PEP 的副作用,内部警告过滤器列表将开始允许使用纯字符串作为过滤器定义的一部分(除了现有的编译正则表达式的使用之外)。当存在时,纯字符串将仅用于精确匹配。此方法允许在解释器启动期间添加新的默认过滤器,而无需提前访问 re 模块。

动机

[1] 中所讨论并在 [2] 中所提及,Python 2.7 和 Python 3.2 更改了 DeprecationWarning 的默认处理方式,以便

  • 默认情况下,在正常代码执行期间隐藏警告。
  • unittest 测试运行器已更新,以便在运行测试时重新启用它。

这样做是为了避免出现以下类似工具输出的情况。

$ devtool mycode/
/usr/lib/python3.6/site-packages/devtool/cli.py:1: DeprecationWarning: 'async' and 'await' will become reserved keywords in Python 3.7
  async = True
... actual tool output ...

即使devtool 是专为 Python 程序员设计的工具,但这也不是一个特别有用的警告,因为它会在每次调用时显示,即使最终用户可以采取的主要有帮助的步骤是向 devtool 的开发人员报告错误。

对于跨多种语言使用的通用开发工具,此警告甚至更没有帮助,并且对于碰巧是用 Python 编写的应用程序(不一定面向开发人员受众)几乎完全*没有*帮助。

但是,此更改被证明对以下受众产生了意外的后果。

  • 任何使用除 unittest 内置的默认测试运行器之外的测试运行器的人(从未明确要求第三方测试运行器更改其默认警告过滤器,因此其中许多仍然依赖于旨在适合已部署应用程序的解释器默认值)。
  • 任何使用默认 unittest 测试运行器在子进程中测试其 Python 代码的人(因为即使 unittest 也只调整当前进程中的警告设置)。
  • 任何在交互式提示符下或作为直接执行的脚本的一部分编写 Python 代码的人,这些脚本根本没有 Python 级别的测试套件。

在这些情况下,DeprecationWarning 最终变得几乎等同于 PendingDeprecationWarning:它根本没有被看到。

PEP 范围的限制

本 PEP 的存在是为了专门解释提议添加到 3.7 默认警告过滤器中的内容,*以及*更清楚地阐明在 Python 2.7 和 3.2 中最初更改 DeprecationWarning 处理方式的理由。

本 PEP 并未解决当前处理弃用警告方法的所有已知问题。最值得注意的是

  • 默认 unittest 测试运行器目前不报告在模块导入时发出的弃用警告,因为警告过滤器覆盖仅在测试执行期间实施,而不是在测试发现和加载期间实施。
  • 默认 unittest 测试运行器目前不报告子进程中的弃用警告,因为警告过滤器覆盖直接应用于加载的 warnings 模块,而不是应用于 PYTHONWARNINGS 环境变量。
  • 标准库没有提供一种简单的方法来选择加入查看在升级特定依赖项之前发出的所有警告(第三方 warn 模块 [3] 提供了此功能,但启用它需要修改标准库的 warnings 模块)。
  • 当软件已被分解成支持模块,但这些模块几乎没有或根本没有自动测试覆盖率时,在 __main__ 中默认重新启用弃用警告不太可能帮助发现 API 兼容性问题。短期来看,目前最好的答案是使用 PYTHONWARNINGS=default::DeprecationWarningpython -W default::DeprecationWarning 运行受影响的应用程序,并注意其 stderr 输出。从长远来看,这实际上是从事 Python 代码静态分析的研究人员的问题:如何可靠地找到弃用 API 的用法,以及如何根据 warnings.warn 调用推断 API 或参数已弃用,而无需实际运行提供 API 的代码或访问它的代码。

虽然这些确实是现状的真正问题,但它们被排除在本 PEP 中的考虑范围之外,因为它们需要比默认警告过滤器中的单个附加条目更复杂的解决方案,并且解决它们至少可能不需要经过 PEP 流程。

对于任何有兴趣进一步探讨这些问题的人,前两个将是 unittest 模块增强请求,第三个将是 warnings 模块增强请求,而最后一个只有在从其内容推断 API 弃用被认为是一个难以解决的代码分析问题时,并且建议在注释中使用显式函数和参数标记语法时,才需要 PEP。

CPython 参考实现还将在 3.7 中包含以下相关更改。

独立于本 PEP 中对默认过滤器的提议更改,问题 32229 [7] 是一项提议,建议添加 warnings.hide_warnings API,以便应用程序开发人员能够更轻松地在正常运行期间隐藏警告,同时在测试时轻松使其可见。

参考文献


来源:https://github.com/python/peps/blob/main/peps/pep-0565.rst

上次修改:2023-10-11 12:05:51 GMT