如何让distutils包仅为构建类命令设置setup_requires?
解决distutils/setuptools中仅构建命令加载大依赖的问题
这确实是distutils/setuptools里一个挺烦人的坑——把体积大的依赖塞到setup_requires里的话,不管你跑啥命令(哪怕只是setup.py clean)都会先拉取编译这个依赖,完全违背了清理命令的初衷。我之前也遇到过类似情况,分享几个亲测有效的解决办法:
方法一:动态判断命令,按需设置setup_requires
核心思路是通过读取命令行参数,判断当前执行的是不是构建相关命令,只有在需要构建时才把大依赖加入setup_requires。代码示例如下:
import sys from setuptools import setup def main(): # 定义需要加载构建依赖的命令集合 build_related_commands = { 'build', 'build_ext', 'install', 'bdist_wheel', 'bdist_egg', 'develop' } # 获取当前执行的命令(处理多参数场景,比如`setup.py build bdist_wheel`) current_commands = set(sys.argv[1:]) needs_build = bool(current_commands & build_related_commands) or \ any(cmd.startswith('build') for cmd in sys.argv) setup_requires = [] if needs_build: # 仅在构建时添加这个大依赖 setup_requires = ['your-heavy-dependency-package'] setup( name='your-awesome-package', version='1.0.0', setup_requires=setup_requires, # 其他必要参数:packages、ext_modules等 packages=['your_package'], ext_modules=[...], # 如果是C扩展之类的需要构建的模块 ) if __name__ == '__main__': main()
关键细节:
- 我们覆盖了常见的构建类命令:
build(基础构建)、build_ext(编译扩展)、install(安装)、bdist_wheel(生成wheel包)等,你可以根据自己的需求调整这个集合。 - 额外处理了以
build开头的命令(比如自定义的build_custom),避免遗漏特殊场景。 - 这种方式对
pip install也友好:pip执行的是install命令,会自动触发依赖加载,不影响正常安装流程。
方法二:用pyproject.toml分离构建依赖(推荐现代项目使用)
如果你已经在使用PEP 517/518标准的构建流程,可以把构建依赖放到pyproject.toml中,这样只有在实际构建时才会安装这些依赖,而执行setup.py clean这类非构建命令时不会触发。
创建pyproject.toml文件,内容如下:
[build-system] requires = ["setuptools", "wheel", "your-heavy-dependency-package"] build-backend = "setuptools.build_meta"
注意事项:
- 这种方式下,当你直接运行
setup.py clean时,setuptools不会去安装build-system.requires里的依赖(因为不需要构建环境),完美解决你的问题。 - 如果你还在维护老项目,可能需要先确认你的构建工具(比如pip版本)支持
pyproject.toml(pip 19.0+已经完全支持)。
方法三:重载命令类(进阶用法)
如果你需要更精细的控制,可以重载distutils/setuptools的命令类,在特定命令执行前才安装依赖。比如重载build_ext命令:
from setuptools import setup from setuptools.command.build_ext import build_ext import subprocess import sys class CustomBuildExt(build_ext): def run(self): # 在执行扩展编译前,手动安装大依赖 subprocess.check_call([sys.executable, "-m", "pip", "install", "your-heavy-dependency-package"]) super().run() setup( name='your-awesome-package', version='1.0.0', cmdclass={'build_ext': CustomBuildExt}, # 其他参数... )
这种方式的灵活性更高,但需要你对distutils的命令体系有一定了解,适合复杂场景。
内容的提问来源于stack exchange,提问作者skoy
相关产品推荐
相关产品推荐

