DEBUG.WIKI
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
雨云;给项目一台云服务器
晚安云;香港 CN2 ¥15/月

Numba 在 Windows ZIP 导入缓存函数,报包内路径 KeyError

Numba 0.68.0 用 Windows 分隔符查找 ZIP 内的源码,可能在 cache=True 装饰阶段抛 KeyError。正式版可改为普通文件部署或停用该函数磁盘缓存,rc2 修正仍属预发布。

Windows 上将带有 @njit(cache=True) 的模块放进 ZIP,再通过 sys.path 导入,Numba 0.68.0 可能在装饰函数时就抛出:

KeyError: "There is no item named 'mypkg\\mod.py' in the archive"

ZIP 里实际存在的文件名却是 mypkg/mod.py。错误出现在读取缓存所需的源码指纹时,业务函数甚至还没有开始运行。

Numba 0.68.0 的 ZipCacheLocator 用本机 Path 拼接压缩包内部路径。Windows 得到反斜杠,而 ZIP 成员名使用正斜杠;后续按这个错误名称调用 ZipFile.open() 就找不到成员。磁盘上的 ZIP 路径可以使用 Windows 分隔符,包内部的成员名却不能照搬同一规则。

先区分包内源码读取与缓存目录写入

查看完整堆栈。如果路径依次经过 get_source_stamp、_hash_zipped_source_file,最后在 zipfile 的成员查找处报上述 KeyError,才符合这项问题。不要看到“缓存”就先删除所有 .nbc 或修改缓存目录权限:找不到的是 ZIP 内的源码成员,不是编译产物。

对于自己生成、路径已确认的包,可以只读列出成员名:

from zipfile import ZipFile

with ZipFile("pkg.zip") as archive:
    print(archive.namelist())

将 pkg.zip 换成实际部署包;这段不会解压或执行里面的文件。若模块本来就未打包进去,或包目录结构与导入名不符,应先修正打包清单,不能把所有 ZIP 导入错误归入 Numba。

正式版还需保留规避方案

截至当前核对,PyPI 最新正式版仍是 0.68.0;0.68.1rc2 已改用正斜杠构造内部路径,但它是候选发行版。继续使用正式 0.68.0 的环境,仍需在部署方式与磁盘缓存之间选择规避方案。

生产暂留 0.68.0 时,较直接的处理是让带 JIT 缓存的 Python 模块以普通文件方式部署,再从正常包目录导入。由既有构建流程把受信任的包内容安装到目标环境,保留原部署包和回退入口;不要为修一个导入错误就在运行时解压任意外来 ZIP。

若能修改自己的函数,且可以接受每个新进程重新编译,也可以暂时取消这项磁盘缓存:

from numba import njit

@njit(cache=False)
def add(a, b):
    return a + b

这保留 JIT 编译,只关闭该函数的跨进程磁盘缓存;它不同于禁用整个 Numba JIT。依赖包中其他 cache=True 函数仍可能在导入时失败,不能只改一个未被实际调用的示例。对第三方包,应使用其支持的部署或配置方式,不在运行中的 Numba 私有类上打猴子补丁。

如果选择在隔离环境评估 0.68.1rc2,要同时核对 Python、NumPy 和 llvmlite 的支持范围,再决定是否接受候选版。正式版将来发布后,仍以具体发行内容为准。

处理后从同一个 Windows 部署入口重新导入包,并检查原函数结果。保留磁盘缓存的方案还要启动第二个进程验证缓存读取;取消缓存的方案则应记录启动编译成本,避免把“能导入”误当成原有启动性能已经恢复。