Poetry打包如何包含不在项目子路径下的Python包/模块
问题背景
单代码仓库存放2个Python项目,二者依赖同一个共享工具包,目标是将两个项目分别打包为.tar.gz格式的软件分发包。此前通过setuptools配合setup.py实现该需求,但操作流程繁琐,希望改用Poetry完成两个项目的独立依赖管理与打包工作。
现有目录结构
repo project1/ __init__.py main_module.py pyproject.toml project2/ __init__.py main_module.py pyproject.toml util/ __init__.py util_module.py
已尝试操作与报错
为实现Poetry构建project1时将上级目录的util包纳入打包范围,修改了project1目录下的pyproject.toml配置:
[tool.poetry] name = "project1" version = "0.1.0" description = "" authors = [""] packages = [ { include = "../util/*.py" } ] [tool.poetry.dependencies] python = "^3.9" [tool.poetry.dev-dependencies] pytest = "^5.2" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api"
执行poetry build命令时抛出如下错误:
Building project1 (0.1.0) - Building sdist ValueError 'C:\\repo\\util\\__init__.py' is not in the subpath of 'C:\\repo\\project1' OR one path is relative and the other is absolute. at ~\.pyenv\pyenv-win\versions\3.9.6\lib\pathlib.py:929 in relative_to 925│ n = len(to_abs_parts) 926│ cf = self._flavour.casefold_parts 927│ if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): 928│ formatted = self._format_parsed_parts(to_drv, to_root, to_parts) → 929│ raise ValueError("{!r} is not in the subpath of {!r}" 930│ " OR one path is relative and the other is absolute." 931│ .format(str(self), str(formatted))) 932│ return self._from_parsed_parts('', root if n == 1 else '', 933│ abs_parts[n:])
核心疑问
- Poetry是否原生支持该使用场景?
- 如果支持,遗漏了哪些配置项?
- 如果不支持,有哪些可行方案可实现两个项目独立打包,且两个分发包中都包含共享的util包。
解答
Poetry 原生不支持直接将pyproject.toml所在目录(即项目根目录)之外的文件纳入打包范围。你遇到的报错是Poetry的默认路径校验逻辑导致的:执行构建时,Poetry的构建上下文被严格限制在当前项目目录内,上级目录的文件不在可打包的路径范围内,无论怎么修改include规则都无法绕过该校验。
以下是两种可落地的实现方案:
方案1:将共享util包配置为独立本地依赖(推荐)
这是最符合Python打包规范、长期维护成本最低的方案:
- 进入util目录,执行
poetry init按照引导生成util包自己的pyproject.toml,将util配置为独立的Poetry包,比如命名为common-util - 分别在project1、project2的
pyproject.toml中添加util的本地路径依赖,替换原有错误的packages配置:
[tool.poetry] name = "project1" version = "0.1.0" description = "" authors = [""] # 不需要额外配置include规则,Poetry默认会打包当前项目目录下的代码包 [tool.poetry.dependencies] python = "^3.9" common-util = { path = "../util" } [tool.poetry.dev-dependencies] pytest = "^5.2" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api"
- 分别在两个项目目录下执行
poetry build,Poetry会自动将util包的代码一并打包到最终的分发包中,不需要额外操作。如果后续需要将项目发布到公共或私有PyPI源,只需要将util包也发布到对应源,把本地路径依赖替换为版本号依赖即可,业务代码不需要任何修改。
方案2:构建前临时复制共享包(临时适配方案)
如果不想拆分util为独立包,可以通过构建脚本临时复制文件的方式实现,不需要调整现有业务代码结构:
- 修改project1的
pyproject.toml的packages配置,去掉跨上级目录的include规则,改为包含当前目录下的util包:
packages = [ { include = "project1" }, { include = "util" } ]
- 编写简单的构建脚本,在构建前将util目录复制到当前项目目录,构建完成后删除临时文件即可。以Linux/macOS的shell脚本为例:
#!/bin/bash # 进入project1目录 cd project1 # 临时复制util包到当前构建目录 cp -r ../util ./util # 执行打包 poetry build # 清理临时文件 rm -rf ./util
- project2的构建脚本逻辑完全一致,只需要把路径替换为project2的目录即可。Windows环境可以编写对应的bat/PowerShell脚本实现相同逻辑。该方案的缺点是需要额外维护构建脚本,跨平台使用时需要做适配。
内容的提问来源于stack exchange,提问作者urig
相关产品推荐
相关产品推荐

