pytest --mypy 默认按Python 3.8模式运行,导致int | str等3.10+语法被忽略、mypy.ini未加载、第三方库类型缺失不报错;需显式配置python_version=3.10、strict=True并安装types-*存根。

直接用 pytest --mypy 就能跑,但默认不检查 Python 3.10+ 新语法(比如 int | str),也不读你的 mypy.ini 配置,更不会报出第三方库的类型缺失问题——这些坑都得手动填。
为什么 pytest --mypy 在 Python 3.10+ 下容易“静默失败”
Pytest-mypy 插件本身不硬编码 Python 版本,它把版本控制完全交给底层 mypy。如果你没显式指定 python_version,mypy 默认按 Python 3.8 兼容模式运行,导致:
-
int | str被当成语法错误,而不是合法联合类型 -
match-case块里的类型推导失效 - 泛型简写如
list[str](而非List[str])被忽略或误报 - 即使你写了
mypy.ini,pytest-mypy 也可能不自动加载它(尤其在子目录运行时)
必须设置 python_version 并启用 strict 模式
在项目根目录建 mypy.ini,内容至少包含:
[mypy] python_version = 3.10 strict = True disallow_untyped_defs = True check_untyped_defs = True
注意:strict = True 不是开关,而是快捷方式——它会自动开启二十多项检查项(包括 warn_return_any、disallow_incomplete_defs 等)。如果你只想开部分,就别用它,逐项写清楚。
立即学习“Python免费学习笔记(深入)”;
关键点:
- 路径必须是项目根目录,且 pytest 必须从该目录下运行(否则
mypy.ini可能被跳过) - 不要写成
python_version = "3.10"—— mypy 会静默忽略带引号的值 - 若项目实际用 3.11 或 3.12,这里必须严格匹配,否则
Literal、Self等新类型无法识别
第三方库类型缺失:types-* 包不是可选,是刚需
像 requests.get() 返回什么类型?mypy 默认只看到 Any。不装 stub 包,90% 的外部调用都会逃过检查。
执行以下命令安装常用库的类型存根:
pip install types-requests types-python-dateutil types-pytz types-Pillow
常见误区:
-
types-xxx名称必须和原包名完全一致(pip install types-Flask,不是types-flask) - 有些库已有内置类型(如
numpy1.21+、django4.1+),装了反而可能冲突 - 若用 Miniconda,建议用
conda install -c conda-forge mypy types-requests保持环境一致性
只跑类型检查,不执行测试函数
开发中常需要快速验证类型改动,又不想等所有单元测试跑完。用这个命令:
pytest -m mypy
前提是你的类型测试函数打了 @pytest.mark.mypy 标签,例如:
import pytest <p>def test_add_type_hints() -> None: assert add(1, 2) == 3</p><p>@pytest.mark.mypy def test_add_signature() -> None:</p><h1>mypy 会检查这个函数所在文件的类型注解</h1><pre class='brush:python;toolbar:false;'>pass</pre>
注意:@pytest.mark.mypy 函数体必须为空(或只含 pass),否则 pytest 会尝试执行它;mypy 实际检查的是整个模块,不是这个函数本身。
另一个更干净的方式是直接绕过 pytest:
- 改用
mypy --show-traceback .查看详细错误位置 - CI 中推荐用
pytest --mypy --tb=short,避免长 traceback 冲刷关键错误行
最易被忽略的一点:mypy 的缓存机制在 pytest-mypy 中默认开启,但不会自动清理。改了 mypy.ini 后,第一次运行可能仍沿用旧配置——加 --clear-cache 强制刷新,或者删掉 .mypy_cache/ 目录再试。

















