谁应指定wheel为Python包依赖?SageMaker容器场景的疑问
我完全懂你在受限容器环境里搞Python依赖时踩的这些坑——尤其是面对带C++扩展的包,加上老版本Python环境的限制,确实容易遇到这类轮子相关的问题。咱们逐个拆解你的疑问:
1. 谁来指定wheel依赖才合理?kiwisolver应该声明它需要wheel吗?
理论上,kiwisolver这类带二进制扩展的包,根本不应该让用户来处理wheel的问题。正确的做法是包的维护者提前预编译好wheel包(比如针对不同平台、Python版本的二进制wheel)并发布到PyPI,这样用户安装时直接下载现成的二进制包,不需要编译,自然也不需要wheel工具。
那为什么会出现需要wheel的情况?如果kiwisolver只发布了源码包(sdist),pip在安装时会默认尝试构建一个wheel缓存(避免下次重复编译),这时候就需要环境里有wheel工具。但这不是kiwisolver本身的构建依赖——kiwisolver的setup.py不需要在setup_requires里声明wheel,因为wheel是pip用来做缓存的工具,不是编译kiwisolver代码必须的组件。
2. 框架应用/环境是否有义务提供wheel?
从Python打包的最佳实践来说,生产级的环境(比如SageMaker容器)应该默认预装wheel。因为从pip 19.0开始,wheel就成了推荐的分发格式,能大幅减少安装时间、避免编译失败的问题。
不过严格来说,没有强制的“义务”,但易用性层面,提供wheel是合理的。你遇到的情况应该是这个特定的TensorFlow Serving容器比较老旧(Python3.5已经停止维护多年),所以没有包含wheel工具。
3. 我必须自己知晓间接依赖需要wheel,才能解决pip安装失败的问题吗?
是的,在这种老环境+依赖仅提供源码包的场景下,你确实需要手动干预。因为pip默认会尝试构建wheel缓存,而环境里没有wheel工具时,就会抛出invalid command 'bdist_wheel'的错误。
你用setup.py加setup_requires=["wheel"]的方法是可行的,因为setup_requires会在构建你的包之前,先安装好wheel工具,这样pip就能正常处理kiwisolver的编译和缓存了。另外还有个更简单的替代方案:直接在你的requirements.txt最顶部加上一行wheel,让pip在安装其他依赖前先装好wheel,同样能解决问题。
4. PEP 518和setup_requires弃用后,答案会有变化吗?
当然会。PEP 518引入了pyproject.toml文件来统一管理构建依赖,替代了老旧的setup_requires。按照新规范,你只需要在项目根目录创建pyproject.toml,写入:
[build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta"
这样pip在构建任何包(包括你的脚本和间接依赖)之前,都会自动安装指定的构建工具,完全不需要依赖setup.py里的setup_requires。
不过要注意,Python3.5对pyproject.toml的支持有限——需要pip版本>=18.0才能识别这个文件。如果你的SageMaker容器里的pip版本不够新,可能还是得用setup_requires的老方法过渡。
内容的提问来源于stack exchange,提问作者dingus

