pytest 8.0+ 是 Python 3.12 的硬性门槛,因旧版依赖已移除的 collections.Mapping 和 distutils/imp 模块,必须明确升级至 pytest≥8.0.0 并排查插件锁定;parametrize 参数为空或 pkg_resources 警告均需针对性修复。

pytest 8.0+ 是硬性门槛,不是可选项
Python 3.12 启动 pytest 就报 ImportError: cannot import name 'Mapping' from 'collections'?说明你还在用 pytest ≤7.4.x。这不是警告,是直接崩溃——因为 Python 3.12 彻底删掉了 collections.Mapping,而旧版 pytest 没改导入路径。
必须升级到 pytest>=8.0.0,且不能靠 pip install --force-reinstall pytest 蒙混过关:它不会自动升主版本。得明确执行:
pip install --upgrade "pytest>=8.0.0"
升级后还要检查是否被其他包锁死版本:
-
pip show pytest看实际装的是不是 8.x -
pipdeptree | grep pytest查有没有pytest-cov或pytest-mock这类老插件在拖后腿
pytest-pythonpath 插件已物理消失,别再试了
import pytest_pythonpath 直接报 ModuleNotFoundError: No module named 'distutils' 或 'imp'?这不是兼容性问题,是模块被 Python 3.12 彻底移除(PEP 594)。你没法 pip install distutils 补救,也没法降级 pytest 来绕过——只要解释器是 3.12,它连 import 都失败。
立即学习“Python免费学习笔记(深入)”;
替代方案只有三个,按推荐顺序:
- 项目根目录放
pyproject.toml,写入[tool.pytest.ini_options]+pythonpath = ["src", "tests"](仅pytest>=8.2支持) - 命令行显式指定:
pytest --import-mode=importlib -c pyproject.toml,避免隐式修改sys.path - 终极干净解法:
pip install -e .把项目本身安装为可导入包,靠pyproject.toml#[project]管依赖和入口
parametrize 参数为空导致 TypeError,不是 pytest 的锅
升级 pytest 8.x 后突然报 TypeError: 'NoneType' object is not iterable,堆栈指向 @pytest.mark.parametrize?这是 Python 3.12 对迭代器求值更严格暴露的逻辑缺陷。
典型场景:@pytest.mark.parametrize("x", some_func()) 中 some_func() 在某些分支返回 None。3.11 可能“容忍”,3.12 在标记解析阶段就要求第二个参数必须是可迭代对象。
修复方式很直接:
- 确保
some_func()总是返回list、tuple或生成器,比如加一行return [] if result is None else result - 临时加断言快速定位:
assert isinstance(result, (list, tuple, types.GeneratorType))
DeprecationWarning: pkg_resources 来自第三方插件
运行 pytest 8.2+ 时看到 pkg_resources is deprecated 警告?这基本不是 pytest 自身的问题——它早在 8.0 就迁移到 importlib.metadata。问题出在你装的某个插件上,比如老版 pytest-html 或 pytest-xdist 还在用 pkg_resources.get_distribution()。
定位来源最有效的方法:
python -W error::DeprecationWarning -m pytest
让警告变异常,看 traceback 最顶上是哪个模块。然后针对性升级那个插件,或换用维护更活跃的替代品(比如用 pytest-html ≥4.1.0)。
真正麻烦的不是找不到补丁,而是把 pythonpath 当万能胶去粘导入问题——在 Python 3.12 下,这种路径污染反而会让依赖关系更难追踪,比显式安装或动态导入更易出错。


















