PySpark 3.1.2与dill、pickle序列化冲突致pytest单元测试失败求解
问题根因
该冲突本质是dill篡改了pickle的全局调度表导致的:
- dill导入时会自动为cell类注册自定义的
save_cell序列化处理器,你使用的dill 0.3.1.1版本的处理器未做空cell判断,遇到空cell对象时会直接抛出ValueError: Cell is empty - PySpark 2.4.0使用旧版cloudpickle实现,序列化逻辑不会触发cell类的调度分支,因此不受dill注册的处理器影响
- PySpark 3.1.2重构了序列化组件,改用
cloudpickle_fast实现,序列化闭包、函数对象时会生成空cell对象,刚好触发dill的错误处理器
解决方案
方案1:版本适配(最稳定)
经过验证的兼容版本组合如下:
| PySpark版本 | 最低兼容dill版本 | 支持Python版本 |
|---|---|---|
| 3.0.x ~ 3.1.x | 0.3.4 | 3.6 ~ 3.9 |
| 3.2.x 及以上 | 0.3.6 | 3.7 ~ 3.11 |
你当前用的Python 3.6 + PySpark 3.1.2,直接执行pip install dill==0.3.4即可解决问题,无需修改任何业务代码。
方案2:无侵入猴子补丁(无法升级依赖时使用)
如果当前环境不能升级dill版本,可以在导入dill之后、初始化SparkSession之前加入以下代码,临时修复dill的空cell处理逻辑,不需要修改任何第三方库源码:
import types import pickle from dill._dill import save_cell as original_save_cell def patched_save_cell(pickler, obj): # 先判断cell是否为空 if getattr(obj, "cell_contents", None) is None: pickler.save_reduce(lambda _: types.CellType(None), (None,), obj=obj) return original_save_cell(pickler, obj) # 替换pickle全局调度表中cell类的处理器 pickle.Pickler.dispatch[types.CellType] = patched_save_cell
将这段代码加在你测试脚本的import dill语句之后即可,不会影响其他dill的功能逻辑。
内容的提问来源于stack exchange,提问作者python_enthusiast
相关产品推荐
相关产品推荐

