如何将 pytest 配置为 raise/trigger BytesWarning?

How to configure pytest to raise/trigger BytesWarning?

也许我做错了,因为我的搜索没有找到任何有用的东西。

在调用 python 解释器时添加 -b (-bb) option 将在隐式字节到字符串或字节到 int 转换发生时发出警告(引发):

Issue a warning when comparing bytes or bytearray with str or bytes with int. Issue an error when the option is given twice (-bb).

我想用 pytest 围绕这个编写一个单元测试。即,我想做

# foo.py

import pytest

def test_foo():
    with pytest.raises(BytesWarning):
        print(b"This is a bytes string.")

当调用上面的 pytest foo.py 时,测试将失败(没有 BytesWarning 引发)。当我将上述测试称为 python -bb -m pytest foo.py 时,它将通过,因为 BytesWarning 作为异常引发。到目前为止一切顺利。

我无法解决的问题(我似乎也无法在互联网上找到任何有用的东西)是 if/how 可以将 pytest 配置为自动执行此操作,以便我可以 运行 pytest --some_arg foo.py 它会做预期的事情。这可能吗?

当您执行 pytest foo.py 时,您的 shell 将查找 pytest 程序。你可以通过命令which pytest知道将执行哪一个。对我来说,/home/stack_overflow/venv/bin/pytest 看起来像这样:

#!/home/stack_overflow/venv/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(console_main())

它只是从 pytest 库中调用 console_main()

加个print(sys.argv)就可以看到是怎么调用的了。对我来说,是 ['/home/stack_overflow/venv/bin/pytest', 'so70782647.py']。它匹配从第一行开始的路径,称为 shebang。它指示应如何调用您的程序。在这里,它使用我的 venv 中的 python 指示 运行 文件。
如果我修改行:

#!/home/stack_overflow/venv/bin/python -bb
#                                     ^^^^

现在你的测试通过了。
这可能是一个解决方案,虽然不是很优雅。

您可能会注意到,即使是现在,打印 sys.argv 时也不会出现 -bb。原因在the doc you linked yourself中解释:

An interface option terminates the list of options consumed by the interpreter, all consecutive arguments will end up in sys.argv [...]

因此无法使用 sys.argv.
检查它是否被激活 我发现了一个关于如何从解释器的内部状态中检索它们的问题,以防您有兴趣将其作为测试的先决条件进行检查:. Although, checking for sys.flags.bytes_warning 在我们的例子中更简单 ({0: None, 1: '-b', 2: '-bb'}).

继续你的问题,如何使用 -bb 解释器选项 运行 pytest?
您已经有了解决方案:python -bb -m pytest foo.py.

如果您愿意,可以创建一个文件 pytest,其内容只是 python -bb -m pytest $@(不要忘记使其可执行)。 运行 它与 ./pytest foo.py。或者将其设为 alias.

你不能告诉 pytest 你想要哪个 Python 解释器选项,因为 pytest 已经 运行ning 在解释器本身中,它已经处理了它自己的选项。

据我所知,这些选项确实不容易改变。我想如果您可以写入 PyConfig C 结构,它就会产生预期的效果。例如,请参阅 function bytes_richcompare 执行 :

        if (_Py_GetConfig()->bytes_warning && (op == Py_EQ || op == Py_NE)) {
            if (PyUnicode_Check(a) || PyUnicode_Check(b)) {
                if (PyErr_WarnEx(PyExc_BytesWarning,
                                 "Comparison between bytes and string", 1))
                    return NULL;
            }

然后您可以在测试中激活它,如:

def test_foo():
    if sys.flags.bytes_warning < 2:
        # change PyConfig.bytes_warning
    assert sys.flags.bytes_warning == 2
    with pytest.raises(BytesWarning):
        print(b"This is a bytes string.")
    # change back the PyConfig.bytes_warning

但我认为如何做到这一点应该是另一个问题。

作为解决方法,您可以像这样使用 pytest.warns

def test_foo():
    with pytest.warns(BytesWarning):
        print(b"This is a bytes string.")

它只需要 -b 选项(尽管 -bb 也可以)。