setup.py中用sys.version_info判断版本会失效,因其依赖构建机运行时版本而非目标环境;应改用pyproject.toml声明式配置、wheel元数据标识及build_ext子类中动态获取目标上下文。

为什么 setup.py 里用 sys.version_info 判断 Python 版本会失效
很多老脚本在 setup.py 里直接写 if sys.version_info >= (3, 8): 来控制 C 扩展编译选项,但这类逻辑在跨版本打包时极易出错——因为 setup.py 是在**当前运行的 Python 解释器下执行的**,不是目标环境。你用 Python 3.9 跑 python setup.py bdist_wheel,它永远看不到 3.7 的限制;反过来,用 3.7 构建时又可能因缺失新 API 报错。
真正需要区分的是 wheel 元数据中的 python_version(如 cp37、cp311),而不是构建机的运行时版本。
- 改用
pyproject.toml+setuptools声明式配置,把平台/版本约束移到[project.optional-dependencies]或[build-system]外部 - 若必须动态控制 C 扩展行为(如条件启用
PyUnicode_AsUTF8AndSize),把判断逻辑下移到setup.py的build_ext子类中,并通过self.compiler.compiler_type和self.get_finalized_command('build_py').build_lib获取真实目标路径上下文 - 避免在
setup()调用前做任何依赖sys.version_info的 import 或计算
如何让同一个 pyproject.toml 在 3.7–3.12 下都生成合规 wheel
pyproject.toml 本身不感知 Python 版本,但它的构建后端(如 setuptools、scikit-build-core)和 C 构建工具链(cmake、meson)对不同 Python 版本的支持差异很大。常见陷阱是:在 3.12 上用了 setuptools>=68.0 的新钩子,却忘了 3.7 只支持到 setuptools==65.5.1。
- 在
[build-system]中锁定最低兼容的requires,例如:requires = ["setuptools>=65.5.1", "wheel"],而非">=68" - 用
python_requires = ">=3.7, !=3.9.7"显式排除已知有问题的小版本(如 3.9.7 的 ABI bug) - 若用
scikit-build-core,必须指定requires-python = ">=3.8"并在[project.optional-dependencies]中为旧版本提供降级 fallback 编译配置 - 所有 C 扩展的
setup.py或CMakeLists.txt必须通过PYTHON_VERSION环境变量或sysconfig.get_config_var("VERSION")获取目标解释器版本,而非sys.version
pip wheel --no-deps --no-cache-dir 为何仍可能复用错误的 C 编译缓存
即使加了 --no-cache-dir,pip wheel 仍可能复用之前构建的 build/ 目录(尤其在 CI 中未清理工作区),而该目录里的 temp.linux-x86_64-3.9/ 这类路径名隐含了构建时的 Python 版本,导致后续用 3.11 构建时误链接旧对象文件。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
- 强制每次构建使用独立输出目录:
pip wheel . --wheel-dir dist/311 --build-dir build/311 --no-deps - 在 CI 脚本中显式
rm -rf build/ dist/,不要依赖--no-cache-dir - 检查生成的 wheel 文件名是否含正确 tag:
mylib-1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl—— 若出现cp39却声称支持 3.11,说明构建环境污染了 - 用
auditwheel show dist/*.whl(Linux)或delvewheel show dist/*.whl(Windows)验证内部链接的 Python ABI 是否匹配声明
Windows 上 vcvarsall.bat 和 MSVCCompiler 的版本错配问题
Python 官方二进制分发版在 Windows 上严格绑定 MSVC 工具链版本:3.7–3.9 用 VS2015/2017(toolset v141),3.10–3.11 用 VS2019(v142),3.12+ 用 VS2022(v143)。如果用 VS2022 编译器去构建标称支持 3.9 的 wheel,会导致 ImportError: DLL load failed while importing _myext: The specified module could not be found.
- 不要全局设置
SET DISTUTILS_USE_SDK=1或SET MSSdk=1,这会让setuptools绕过自动 toolset 探测 - 在 CI 中按 Python 版本切换构建镜像:Python 3.9 →
windows-2019+vs2017;Python 3.12 →windows-2022+vs2022 - 若需单机多版本构建,用
py -3.9 -c "import setuptools; setuptools.build_meta"触发对应 Python 的build_meta,确保MSVCCompiler实例由目标解释器初始化 - 检查
python -c "import sysconfig; print(sysconfig.get_config_var('MSSDK'))"输出是否与当前 VS 版本一致
最易被忽略的是:C 扩展的 __init__.py 中若用了 from .cext import *,而 cext 的 pyd 文件在加载时因 ABI 不匹配静默失败,最终只报 ModuleNotFoundError,掩盖了真正的 ABI 问题。

















