You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何mypy会对assert does_not_raise抛出truthy-function错误?

解决mypy对assert does_not_raise的类型检查错误

问题背景

接手离职开发者的项目后,遇到一段测试代码触发了mypy的类型检查错误,不想通过# type: ignore跳过检查,希望从根源解决问题。

测试代码

from contextlib import nullcontext as does_not_raise

def test_validation_raised_no_error_when_validation_succeeds():
    # given
    given_df = DataFrame(data={"foo": [1, 2], "bar": ["a", "b"]})
    given_schema = Schema(
        [
            Column("foo", [InListValidation([1, 2])]),
            Column("bar", [InListValidation(["a", "b"])]),
        ]
    )
    # when
    _validate_schema(given_df, given_schema)

    # then
    assert does_not_raise  # line 251

mypy错误信息

test/clients/test_my_client.py:251: error: Function "Type[nullcontext[Any]]" could always be true in boolean context  [truthy-function]
Found 1 error in 1 file (checked 24 source files)

mypy配置

[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
ignore_missing_imports = True

问题原因

does_not_raise是nullcontext的别名,本质是一个类(类型对象)。在Python中,所有非空的类型对象在布尔判断里都会被视为True,所以assert does_not_raise等价于assert True——这行代码完全起不到验证_validate_schema是否真的没抛出异常的作用,mypy正是发现了这个逻辑冗余才报错。

原开发者显然混淆了does_not_raise的正确用法:它是用来和pytest.raises配合,在参数化测试中统一断言逻辑的上下文管理器,不是直接用来assert的对象。


正确解决方式

方式1:移除无效断言

如果_validate_schema执行时抛出异常,测试本身就会直接失败,不需要额外的断言语句。直接删掉assert does_not_raise这行代码即可,测试逻辑完全不受影响。

修改后的测试代码:

from contextlib import nullcontext as does_not_raise

def test_validation_raised_no_error_when_validation_succeeds():
    # given
    given_df = DataFrame(data={"foo": [1, 2], "bar": ["a", "b"]})
    given_schema = Schema(
        [
            Column("foo", [InListValidation([1, 2])]),
            Column("bar", [InListValidation(["a", "b"])]),
        ]
    )
    # when
    _validate_schema(given_df, given_schema)

    # then - 无需额外断言,无异常即测试通过

方式2:正确使用does_not_raise上下文(适合参数化场景)

如果后续需要扩展成参数化测试(比如同时测试“无异常”和“有特定异常”的场景),可以用上下文管理器包裹执行逻辑,示例:

import pytest
from contextlib import nullcontext as does_not_raise

@pytest.mark.parametrize(
    "df, schema, expectation",
    [
        # 无异常的用例
        (
            DataFrame(data={"foo": [1, 2], "bar": ["a", "b"]}),
            Schema([Column("foo", [InListValidation([1, 2])]), Column("bar", [InListValidation(["a", "b"])])]),
            does_not_raise(),
        ),
        # 有异常的用例
        (
            DataFrame(data={"foo": [3], "bar": ["c"]}),
            Schema([Column("foo", [InListValidation([1, 2])]), Column("bar", [InListValidation(["a", "b"])])]),
            pytest.raises(ValidationError),
        ),
    ]
)
def test_validation(df, schema, expectation):
    with expectation:
        _validate_schema(df, schema)

这种写法下,does_not_raise()是实例化后的上下文管理器,和pytest.raises()的用法统一,mypy也不会再报错。

内容的提问来源于stack exchange,提问作者oschlueter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 06:35:30