Poetry本地依赖打包部署求助:无PyPI环境下安装异常问题
本地依赖包部署问题
项目结构
Project |---PackageA |------dist |----------packagea.whl |------pyproject.toml |---PackageB |------dist |----------packageb.whl |------pyproject.toml
PackageB依赖PackageA,当前PackageB的pyproject.toml配置如下:
[tool.poetry.dependencies] packagea = {path = "../PackageA", develop = true}
因知识产权限制无法发布到PyPI,将两个wheel文件传到生产机器后,PackageA安装成功,但PackageB安装失败——它仍在寻找开发机器上PackageA的原路径。
三个疑问
- 基础疑问:安装PackageA后,
pip list能看到packagea已安装,为什么安装PackageB时不跳过该依赖的安装? - 无PyPI环境下的正确构建分发方式:没有PyPI的情况下,怎么正确构建并分发包?
- 安装时排除指定依赖:能否在安装packageb.whl时仅排除packagea依赖?毕竟环境里已经有packagea,运行不会有问题。
后续探索
目前未找到无需外部仓库(PyPI或Git)构建两个本地包用于部署的官方合规方式。pip要求依赖使用绝对路径,导致PackageB不具备可移植性——因为无法提前知晓依赖在目标机器上的部署路径。简言之:官方没有无需临时 workaround 就能构建/发布含本地依赖包的方法,这类包无法直接安装。
由于无法预知终端用户机器上依赖wheel文件的位置,自动安装依赖的常规部署不可行。找到两种临时解决方法,可提前安装依赖并让pip认为对应版本已存在:
- 不在toml文件的主依赖列表中添加该依赖,而是通过
-G dev添加到dev分组。构建wheel文件时会忽略dev依赖,没有依赖就不会有问题。 - 修改编译后的wheel文件(本质是归档文件),编辑内部的RECORD文件,使用以下代码更新所需依赖的sha256:
import zipfile import hashlib import os import importlib.metadata import shutil def fix_wheel_dependency(wheel_path, package_name): # 创建临时解压目录 tmp_dir = "tmp_wheel" os.makedirs(tmp_dir, exist_ok=True) # 解压wheel包 with zipfile.ZipFile(wheel_path, 'r') as zf: zf.extractall(tmp_dir) # 定位RECORD文件 record_path = os.path.join(tmp_dir, "RECORD") if not os.path.exists(record_path): print("RECORD文件不存在") return # 读取RECORD内容 with open(record_path, 'r') as f: lines = f.readlines() # 获取已安装依赖的文件并计算sha256 pkg_dist = importlib.metadata.distribution(package_name) pkg_files = list(pkg_dist.files) if pkg_files: file_path = pkg_files[0].locate() sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block) sha256 = sha256_hash.hexdigest() # 修改RECORD中对应依赖的哈希值 new_lines = [] for line in lines: if package_name in line: parts = line.split(',') if len(parts) >= 2: parts[1] = f"sha256={sha256}" line = ','.join(parts) new_lines.append(line) # 将修改后的内容写回RECORD with open(record_path, 'w') as f: f.writelines(new_lines) # 重新打包生成修复后的wheel new_wheel_path = f"fixed_{wheel_path}" with zipfile.ZipFile(new_wheel_path, 'w', zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(tmp_dir): for file in files: file_path = os.path.join(root, file) arcname = os.path.relpath(file_path, tmp_dir) zf.write(file_path, arcname) # 清理临时文件 shutil.rmtree(tmp_dir) print(f"修复后的wheel已保存到:{new_wheel_path}") # 使用示例 fix_wheel_dependency("packageb.whl", "packagea")
内容的提问来源于stack exchange,提问作者Andrey
相关产品推荐
相关产品推荐

