【问题标题】:How to tell mypy that I am explicitly testing for an incorrect type?如何告诉 mypy 我正在明确测试不正确的类型?
【发布时间】:2023-02-15 08:12:01
【问题描述】:

考虑以下玩具示例:

import pytest


def add(a: float) -> float:
    if not isinstance(a, float):
        raise ValueError("a must be of type float")
    return 10 + a


def test_add_wrong_type() -> None:
    with pytest.raises(ValueError) as err:
        add("foo")  # mypy is complaining here

    assert str(err.value) == "a must be of type float"

mypy 抱怨如下:
“add”的参数 1 具有不兼容的类型“str”;预期的“浮动”[arg-type]

好吧,mypy 是正确的。但是,在那种情况下,我故意输入了错误的类型。我怎样才能告诉mypy忽略这一行`?

换句话说,什么是测试不正确输入类型的 pythonic 方法?

【问题讨论】:

  • # type: ignore[arg-type]
  • 我认为 mypy 的价值在于,当正确集成时,它消除了完全编写此类测试的需要。
  • 无论如何,你都会从10 + a 得到一个ValueError;我不确定仅仅更改与之关联的消息是否值得运行时成本。
  • cast(float, "foo") 也可以。
  • 实际上 # type: ignore[arg-type] 是这里唯一语义正确的事情:你没有 cast,你没有假装 "foo" 是一个浮点数,你发出的调用从打字的角度来看是无效的。类型检查器会警告您这个事实——但是,您仍然想继续,因为这是一个测试,并且您正在测试不安全调用的行为。 type: ignore cmets 没有任何问题,python 甚至不是打字稿,当你 @ts-ignore 时,所有 linters 都疯狂地哭泣,直到你禁用这个奇怪的规则集。

标签: python pytest mypy


【解决方案1】:

使用typing.cast 断言"foo"float

from typing import cast


def test_add_wrong_type() -> None:
    with pytest.raises(ValueError) as err:
        add(cast(float, "foo"))

    assert str(err.value) == "a must be of type float"
    

cast 不会打字转换;在运行时,它只返回它的第二个参数。它的目的是提供一种标准方法来断言给定值的静态类型,独立于您可能使用的任何类型检查器。

【讨论】:

  • 这是可行的,但是,我不喜欢的是我必须以非自然的方式调用该函数。它看起来很奇怪。现在,我会选择@anthony sottile 解决方案。
  • 有什么理由使用(非常不自然的)cast而不是type: ignore?它是故意无效的,为什么要试图说服mypy它不是?
  • 我假设 typing.cast 是“更多”标准,在标准库中定义和记录,而不是仅仅在 PEP 484 中被提及。
  • # type: ignore 将无法处理您只想禁用单个参数的类型检查的情况,不是吗?
【解决方案2】:

Python 类型社区至少有两种单元测试类型注释的方法。注意 # type: ignoretyping.cast 不是测试他们 - 他们分别忽略他们和覆盖他们。

根据您的目的,您可能想要执行以下操作之一:

  • stubtest,mypy 附带。这被 typeshed 广泛使用,但更适合检查 .pyi 文件中的任何给定类型注释是否与运行时实现匹配。
  • pytest-mypy-plugins,这听起来像是您在示例中应该追求的。以 README.md 为例,你可能想写这样的东西:
    # test_add.yml
    - case: test_add_wrong_type
      main: |
        from my_module import add
    
        add("")  # E: Argument 1 to "add" has incompatible type "str"; expected "float" [arg-type]
    
    (我还没有对此进行测试——他们的文档没有提到内联测试错误。我从mypy/test-data/unit 中的示例中获取了# E: ... 语法)。

您还可以使 mypy 成为您的 pytest 测试套件的运行时依赖项,在这种情况下,您可能不需要 pytest-mypy-plugins - 他们的测试套件在他们的 own non-exposed pytest hooks 上运行,您可以直接将其导入您的 conftest.py。只需写出示例,例如他们的 check-*.test 文件。

【讨论】:

    猜你喜欢
    • 2020-08-25
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 2018-06-29
    • 2020-05-15
    • 1970-01-01
    • 2019-05-24
    相关资源
    最近更新 更多