You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的原路径。

三个疑问

  1. 基础疑问:安装PackageA后,pip list能看到packagea已安装,为什么安装PackageB时不跳过该依赖的安装?
  2. 无PyPI环境下的正确构建分发方式:没有PyPI的情况下,怎么正确构建并分发包?
  3. 安装时排除指定依赖:能否在安装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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:37:39