ci 中优先用 mypy,因其检查更严格稳定;pyright 更适合开发时实时反馈。两者需统一 python_version 配置,noreturn 应显式标注,第三方 stub 缺失时 mypy 更敏感,配置应按阶段分离。

pyright 和 mypy 哪个更适合 CI 流水线
pyright 更快,mypy 更准——但“快”在 CI 里不等于“好”。pyright 默认跳过未 import 的模块类型检查,pyright --skipunresolved 开启后才接近 mypy 行为;而 mypy 的 --follow-imports=normal(默认)会递归解析所有依赖,容易因第三方包缺失 stub 导致失败。
实操建议:
- CI 中优先用
mypy,尤其当项目已用pyproject.toml配置了[tool.mypy]且团队熟悉其报错风格 - 若用 pyright,必须显式加
--verify-types+--skip-unresolved=false,否则会漏掉from missing_module import X这类硬错误 - 两者都需统一
python_version配置,否则Literal["a", "b"]在 Python 3.8 下可能被 mypy 当成语法错误,pyright 却默许
遇到 NoReturn 报错时该信谁
mypy 对 NoReturn 更严格:函数末尾无 return、抛异常但没标注 -> NoReturn,它会警告“missing return statement”;pyright 则倾向静态推断,只要路径终点是 raise 或 sys.exit() 就默认接受。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
- mypy 报
Missing return statement,但代码实际不会走到末尾(比如最后是assert False) - pyright 不报,但 mypy 在 CI 失败,引发“本地能过、CI 红了”的困惑
解决方法:
- 统一用
typing.NoReturn显式标注函数签名,别依赖推断 - 避免
assert False作终止,改用raise NotImplementedError()—— mypy 认这个,pyright 也认 - 如果用
sys.exit(),加上# type: ignore注释不如直接标注返回类型,更稳定
第三方库类型缺失导致的假阳性差异
两者对 types-* stub 包的依赖程度不同:mypy 强制要求所有导入模块有 stub(哪怕空的 py.typed 文件),pyright 默认容忍无类型模块,只给弱提示。
使用场景:
- 项目用了
requests但没装types-requests:mypy 报error: Module has no attribute "get",pyright 只标灰(any类型)不中断检查 - 团队引入新 SDK,还没发布 stub:mypy 需临时加
[mypy-xxx] ignore_missing_imports = true,pyright 可先不管
参数差异:
- mypy 的
ignore_missing_imports = true是全局开关,关了就全严,开了就全松 - pyright 的
"reportMissingImports": "none"可按文件粒度控制,在pyrightconfig.json里配"include": ["src/"],不影响测试目录
VS Code 插件体验和配置冲突点
VS Code 默认 Python 扩展内置 pyright,开箱即用;mypy 需手动装 mypy + python-mypy 插件,且两者不能共存于同一工作区的实时检查中——会重复报错。
容易踩的坑:
- 同时启用 mypy 插件和 Pylance(含 pyright),编辑器右下角显示两个类型检查器图标,但只有一套诊断结果生效,另一套静默失效
- pyright 的
typeCheckingMode设为basic时,连str.join(list[int])这种明显错误都不报,误以为“没问题” - mypy 的
cache_dir若指向 NFS 或 Docker volume,首次运行极慢,但 pyright 的增量缓存对远程文件系统更友好
实操建议:
- 开发时用 pyright(快 + 实时响应),CI 用 mypy(稳 + 报错一致)——靠
pyproject.toml分离配置,不是靠插件开关 - 禁用 Pylance 的类型检查(
"python.analysis.typeCheckingMode": "off"),只留其智能补全能力,把类型检查权交给独立 mypy 进程
类型检查器不是非此即彼的选择,而是检查阶段、精度粒度、团队习惯的组合。最常被忽略的是:mypy 的 disallow_untyped_defs 和 pyright 的 reportUntypedFunctionDecorator 表面相似,实际约束范围差两层抽象——一个管函数体,一个只管装饰器本身。










