如何为 pytest 固定装置指定 mypy 类型 导入 from _pytest.config自定义类型存根

问题描述

我正在尝试为我在测试项目中使用的 pytest 本机装置指定 mypy 类型提示,例如:

import pytest

def pytest_configure(config):
    # Do something useful here

config 固定装置返回一个 _pytest.config.Config 对象。如果我尝试天真地建模:

import pytest

def pytest_configure(config: Config) -> None:
    # Do something useful here

我收到一个 mypy 错误conftest.py:3: error: Name 'Config' is not defined [name-defined]

我可以做 from _pytest.config import Config,但这似乎不是一个方法,因为 _pytest 是私有的。 另一种选择是使用 # type: ignore 忽略类型。如果这是推荐的方式,我当然会这样做,但我想知道是否有更好的选择。

我在使用任何类型的 pytest 本机装置时都有同样的问题,例如request 用于参数化装置。这将是一个 _pytest.fixtures.FixtureRequest

解决方法

导入 from _pytest.config

由于 pytest 当前不导出 Config(从 6.2 开始),输入的唯一方法是使用 from _pytest.config import Config。这也是我输入 config 的方式,例如可以看到在this question of mine

from _pytest.config import Config

def pytest_configure(config: Config) -> None:
    ...

您可以在此 pytest 问题中跟踪打字进度:#7469

自定义类型存根

您还可以引入一个小的自定义类型存根来隐藏重新导出。它在这里是否有用是值得怀疑的,仅值得一提的是替代解决方案。如果您使用以下内容创建文件 _typeshed/pytest.pyi

from typing import Any
from _pytest.config import Config as Config

def __getattr__(name: str) -> Any: ...  # incomplete

并使其在 mypy 中可供 mypy.ini 访问:

[mypy]
mypy_path = _typeshed

现在您至少可以在类型检查模式下导入 from pytest import Config - 运行时导入仍然会失败。所以进口看起来像

from typing import Any,TYPE_CHECKING

if TYPE_CHECKING:
    from pytest import Config
else:
    Config = Any


def pytest_configure(config: Config) -> None:
    pass

该解决方案的唯一好处是现在隐藏了私有导入;不过我还是会选择私人进口。