DEBUG.WIKI
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

setuptools 84 的 CompileError 捕获不到,检查编译器导入路径

setuptools 84 的私有编译器路径与公共异常导出可能属于不同模块树,同名 CompileError 无法互相匹配。统一构建路径,保留可选功能探测的失败语义。

构建脚本一直用 except CompileError 把缺失头文件当成“某功能不可用”,升级 setuptools 84.0.0 后却直接终止 wheel 构建。若编译器来自 setuptools._distutils.ccompiler,而异常类从 setuptools.errors 导入,先检查两个导入路径是否生成了不同的类对象。

这条回归有 Python 3.12、3.13 以及 macOS、Linux arm64 的触发记录。setuptools 自己的 build_ext 和经正常 distutils 兼容路径创建的编译器不一定受影响;不能把它写成所有 84.0.0 编译异常都捕获失败。

编译失败本来可能只是探测结果

许多构建辅助库会尝试编译一小段包含可选头文件的源码。失败被捕获后,脚本关闭相应功能继续构建;现在同一失败逃出处理器,才让整个 wheel 安装中断。

因此先看完整 traceback 和捕获类的导入位置。setuptools.errors.CompileError 来自 distutils 模块树,私有路径的编译器可能使用 setuptools._distutils 模块树。两棵树可以指向同一份磁盘文件,但 Python 将它们作为不同模块加载,里面定义的同名异常并不相同。

84.0.0 改为相对路径发现编译器,暴露了此前被固定导入路径掩盖的身份差异。单看异常最后一行都叫 CompileError,无法判定原来的 except 应当匹配。

自己维护的构建代码,统一实际使用的路径

在 setuptools 正常提供 vendored distutils 的构建环境里,可以评估沿同一兼容模块树创建编译器和捕获异常:

import setuptools
from distutils.ccompiler import new_compiler
from setuptools.errors import CompileError

compiler = new_compiler()

这段只创建编译器对象,不调用外部编译程序。先导入 setuptools 是为了使用该构建环境的兼容处理;它不表示 Python 3.12 标准库恢复了 distutils。项目如果强制使用另一套 distutils 或有专门构建封装,应先核对相应约定。

真实探测逻辑、编译选项和失败处理应保留,不能把任意编译错误都改成成功。若第三方库内部仍导入私有路径,应更新那个实际构建辅助库,或者在依赖允许范围内恢复此前验证过的构建后端组合;只改外层 setup.py 的一处 import 未必能覆盖它。

截至 2026 年 10 月 26 日,正式 setuptools 84.2.1 仍有这两棵模块树与异常导出路径,不能把未发布的统一方案当作可用修复。构建版本约束应传入真正的隔离构建环境,而不是只安装到外部运行环境。

不要用 except Exception: pass 吞掉所有探测失败。真实的权限、工具链或源码错误也会被隐藏,随后可能生成缺功能的二进制。验收应覆盖“有可选头文件”和“合法缺失可选头文件”两种分支,再核对最终 wheel 可导入、功能开关符合预期。构建成功本身不能证明原本可选的功能仍然启用。