warnings.simplefilter("always") 不强制在 Python 2.7 中发出警告

warnings.simplefilter("always") is not forcing warnings to be made in Python 2.7

warnings.simplefilter('always') 似乎不会导致警告在 Python 2.7 中重复出现。好像和'once'一样的效果。

我正在尝试测试一些发出警告的代码。有一半时间,我试图测试的警告已经发出,所以我的测试使用simplefilter()来确保再次发出警告。这个问题只出现在Python 2.7。 The other versions all seem to work as expected.

我遵循了 Python 2.7 文档中的示例 https://docs.python.org/2/library/warnings.html#testing-warnings 该示例按原样工作,但如果我在测试代码之前添加对 fxn() 的调用,则不会引发警告并且 len(w) = 0 而不是 1.

具体来说,

import warnings

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

fxn()

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)

预期的结果是运行结束没有输出。 Python 2.7.14 和 2.7.15 中的实际结果:

Traceback (most recent call last):
  File "warntest.py", line 14, in <module>
    assert len(w) == 1
AssertionError

更新: 我还发现在发出第一个警告后调用 warnings.resetwarnings() 不起作用。

import warnings

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

fxn()
warnings.resetwarnings()

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)

fxn()

结果:

Traceback (most recent call last):
  File "warntest.py", line 15, in <module>
    assert len(w) == 1
AssertionError

这绝对是 Python 2.7 的错误,但我根据 Tom 的建议实施了 work-around。变通方法导致我的单元测试针对 Python 2.7 发出大量警告,但它通过了。基本上,当 Python 主要版本为 2 时,我添加了

warnings.simplefilter('always')

在考试开始之前,我跳过了个人

warnings.simplefilter('ignore')

我在测试中发现了应该忽略的警告。

在第一次发出警告之前,我处于无法控制过滤器的情况。我需要做的解决方法是粉碎发出警告的记录。这存储在发出警告的模块中的 __warningregistry__ 中。键是一个元组(警告字符串、警告类型、行号)。如果该值为 truthy,则将跳过警告;如果是假的或没有这样的密钥,它将被发行。实际上,警告模块似乎从来没有将其设置为任何不真实的东西。

因此,如果您在 module 中调用可能引发警告的内容,则必要的步骤是:

if sys.version_info[0:2] == (2,7) and hasattr(module, '__warningregistry__'):
    for k in module.__warningregistry__.keys():
        if k[0].startswith(
            'Expected warning string') \
            and k[1] is UserWarning:
                del module.__warningregistry__[k]
                    break
warnings.simplefilter('always')

我承认这简直太糟糕了。