setup.py下载可执行二进制文件并纳入项目包的最佳实践
Python项目集成外部二进制依赖的实现方案
1. 下载、存储可执行二进制文件的行业最佳实践
- 绝对不要在
setup.py执行阶段触发下载:setup.py的执行时机完全不受控,依赖解析、源码构建、包信息查询、常规安装流程都会触发它运行,一旦出现网络波动、下载地址不可达、安装目录无权限的问题,会直接导致整个包安装失败。当前PEP 517/518打包规范明确不鼓励安装流程携带这类网络请求类副作用。 - 优先选择预打包随包分发的方案:如果二进制文件体积可控,最好按操作系统、CPU架构拆分打包成平台专属的wheel包,每个包里内置对应平台的可执行文件,用户安装时直接匹配对应平台的包,全程不需要额外下载,这是稳定性最高的方案。
- 确需动态下载时遵守以下规则:
- 下载时机放在用户第一次调用对应功能时,不要放在安装阶段,避免用户安装包时无预期卡在下载流程
- 不要把下载的文件存到Python包的安装目录:全局安装场景下
site-packages目录通常需要管理员权限,普通用户写入会直接报错。统一存到系统标准用户缓存目录即可,跨平台路径获取可以直接用platformdirs库的user_cache_dir()方法,不需要自己写系统判断逻辑 - 下载流程必须加校验:提前在代码里硬编码对应版本zip包的sha256哈希值,下载完成后先校验哈希,匹配不通过就删除损坏文件、触发重试或抛出明确错误,既可以规避网络波动导致的文件损坏,也能防范链路劫持替换恶意文件
- 网络请求加超时、重试逻辑,同时预留环境变量支持用户自定义下载镜像、本地文件路径,方便内网无公网环境的用户部署
2. 将二进制纳入Python分发包、支持包路径调用的实现方案
该方案完全可行,按以下步骤配置即可:
- 在项目根目录下创建名为
xxx的子包目录,将提前提取好的xxx.exe放入该目录,同时在目录下新建空的__init__.py文件,确保Python可以识别该目录为可导入的包。 - 修改打包配置,告知打包工具需要将exe文件纳入分发包(setuptools默认只会打包
.py后缀的文件):
如果使用setup.py配置,添加如下配置项:
如果使用from setuptools import setup, find_packages setup( # 其余基础配置(名称、版本、作者等)省略 packages=find_packages(), # 确保xxx子包被识别到 package_data={ "xxx": ["*.exe"], # 声明xxx包下的exe文件需要打包进去 }, include_package_data=True, )pyproject.toml做打包配置,在[tool.setuptools.package-data]段添加对应规则即可。 - 代码中调用二进制时,不要硬编码Windows专属的反斜杠路径,用标准库方法做跨平台路径拼接:
import xxx import os import subprocess exe_path = os.path.join(xxx.__path__[0], "xxx.exe") subprocess.Popen([exe_path])
注意:如果要做跨平台兼容,不要把所有平台的二进制都塞到同一个通用wheel包里,最好按平台构建专属wheel,每个包内置对应平台的可执行文件;如果不想拆分多平台包,就采用前面提到的首次运行时按当前平台动态下载对应二进制的逻辑。
内容的提问来源于stack exchange,提问作者Or Bendel
相关产品推荐
相关产品推荐

