Poetry 多 README 文件支持实战:readme 列表配置、源码实现与构建产物验证
【免费下载链接】poetryPython packaging and dependency management made easy项目地址: https://gitcode.com/GitHub_Trending/po/poetry
在 Poetry 项目中,一个包往往只对应一个 README 文件,但当你需要同时维护「用户使用说明」与「变更日志」、或希望把不同语言的说明文档分开管理时,单一readme字段就力不从心。Poetry 为此提供了多个 README 文件的支持:通过tool.poetry.readme列表声明一组文档,构建时将它们合并进发行版的元数据与产物中。本文以仓库中的with_multiple_readme_files测试夹具为锚点,完整讲解多 README 的配置写法、底层源码实现、poetry check校验以及构建后 sdist/wheel 中的实际验证方法,帮助你在真实项目中放心使用这一能力。
一、为什么要支持多个 README 文件
PyPI 与大多数打包工具只接受单个「长描述」(即元数据中的Description字段,等价于 setuptools 的long_description)。但真实项目经常遇到这类诉求:
- README 与 CHANGELOG 分开维护:README 面向使用方保持稳定,CHANGELOG 随版本频繁更新;
- 多语言文档:README 按语言拆分为多个文件;
- 文档目录化组织:把 README 拆进
docs/目录按主题管理。
Poetry 的解法不是让用户自己拼字符串,而是允许在tool.poetry段用列表声明多个 README 路径,构建时按顺序拼接。仓库中的测试夹具 tests/fixtures/with_multiple_readme_files 就是这一特性的最小演示:README-1.rst内容为Single Python(标题占位),README-2.rst内容为Changelog,两者通过pyproject.toml中的列表声明组合为一个包的文档。
二、配置方式:两种表段,两种语义
Poetry 中与 README 相关的配置出现在两个地方,语义不同,需要注意区分。
1.[project]表段:单个 README(PEP 621 标准字段)
标准project表段下,readme只能是一个字符串路径或内联内容:
[project] name = "my-package" version = "0.1" readme = "README.md"局限:project.readme本身不支持列表。若希望在该标准字段下使用多个文件,必须把readme声明为动态字段,再在tool.poetry段提供实际列表(详见 docs/pyproject.md):
[project] name = "my-package" # ... dynamic = ["readme"] [tool.poetry] # ... readme = ["docs/README1.md", "docs/README2.md"]2.[tool.poetry]表段:字符串或列表
tool.poetry.readme可以是一个路径字符串,也可以是一组路径的列表。官方文档明确建议:如果不需要多文件,优先使用project.readme;只有多 README 场景才使用tool.poetry段的列表形式(见 docs/pyproject.md)。
列表写法如下:
[tool.poetry] name = "my-package" version = "0.1" readme = ["README-1.rst", "README-2.rst"]仓库夹具 tests/fixtures/with_multiple_readme_files/pyproject.toml 即采用这种形式,并配合poetry-core作为构建后端:
[tool.poetry] name = "my-package" version = "0.1" description = "Some description." authors = [ "Your Name <you@example.com>" ] license = "MIT" readme = [ "README-1.rst", "README-2.rst" ] [tool.poetry.dependencies] python = "^3.7" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api"路径基准:README 路径隐含地相对于pyproject.toml所在目录解析,因此可以放心写docs/README1.md这类子目录路径。
三、源码级解析:readme 列表是如何被处理与回写的
1. 序列化回写:src/poetry/factory.py
从源码结构看,Factory在把包对象还原为pyproject.toml内容时,会遍历package.readmes列表,将其转换为相对root_dir的 POSIX 路径并写回content["readme"]:
readmes = [] for readme in package.readmes: readme_posix_path = readme.as_posix() with contextlib.suppress(ValueError): if package.root_dir: readme_posix_path = readme.relative_to(package.root_dir).as_posix() readmes.append(readme_posix_path) if readmes: content["readme"] = readmes对应实现见 src/poetry/factory.py。这段逻辑说明:无论用户在配置里写的是相对路径还是绝对路径,Poetry 都会统一归一化为相对pyproject.toml的路径再回写,保证配置的可移植性。
2. 存在性校验:poetry check命令
poetry check会实际校验 README 文件是否存在。_validate_readme方法把字符串形式的单一 README 统一包装成列表后逐一检查(见 src/poetry/console/commands/check.py):
def _validate_readme(self, readme: str | list[str], poetry_file: Path) -> list[str]: """Check existence of referenced readme files""" readmes = [readme] if isinstance(readme, str) else readme for name in readmes: # ... 检查文件存在性,缺失则记录错误校验逻辑同时覆盖[tool.poetry]段的readme与[project]段的readme(支持字符串或{"file": ...}字典形式)。因此,配置多 README 后先跑一次poetry check,能第一时间发现路径写错或文件缺失的问题。
3. 新项目默认生成:src/poetry/layouts/layout.py
poetry new在创建项目时,会根据--readme选项(默认md)生成对应的 README 文件,并写入pyproject.toml的readme字段(见 src/poetry/layouts/layout.py)。这意味着多 README 通常是在项目演进过程中手动改造配置,而非新建时直接生成。
四、构建行为验证:多个 README 是否真的进入发行包
配置完成后,最关心的问题是:多个 README 会不会真正进入构建产物?仓库的测试用例给出了明确答案。
tests/console/commands/test_build.py中的test_build_with_multiple_readme_files(见 tests/console/commands/test_build.py)完整演示了验证流程:
- 复制
with_multiple_readme_files夹具到临时目录; - 用
Factory().create_poetry(...)加载项目并执行build命令; - 断言
dist/下同时生成 sdist(my_package-0.1.tar.gz)与 wheel(my_package-0.1-*.whl); - 打开 sdist 压缩包,断言其中同时包含两个文件:
with tarfile.open(sdist_file) as tf: sdist_content = tf.getnames() assert "my_package-0.1/README-1.rst" in sdist_content assert "my_package-0.1/README-2.rst" in sdist_content同时,tests/masonry/builders/test_editable_builder.py 中也有对应的with_multiple_readme_files夹具,用于验证可编辑安装(editable install)场景下多个 README 的处理。
你可以复现的验证步骤(在任意本地 Poetry 项目中):
# 1. 在 pyproject.toml 中声明多个 README # readme = ["README-1.rst", "README-2.rst"] # 2. 校验配置与文件存在性 poetry check # 3. 构建发行包 poetry build # 4. 查看 sdist 内容,确认两个 README 都已打包 tar -tzf dist/my_package-0.1.tar.gz五、合并规则与元数据细节
1. 元数据拼接
多个 README 的内容会被用来填充发行版元数据的Description字段(对应 PyPI 上的长描述,等价于 setuptools 的long_description)。官方文档明确:当指定多个文件时,它们按声明顺序用换行符拼接(见 docs/pyproject.md)。因此列表顺序就是最终文档的呈现顺序,请把「主说明」放在前面、「附录/变更记录」放在后面。
2. 格式与发布建议
- README 文件可以是任意格式(Markdown、reStructuredText 等),但若打算发布到 PyPI,建议遵循 PyPI-friendly README 的推荐写法;
- 如果希望在 sdist 之外、把 README 同时用于 wheel 的元数据展示,多文件拼接后的整体内容都会进入
Description字段; - 测试夹具中的
README-1.rst与README-2.rst分别使用 reStructuredText 标题语法,说明不同文件可以采用同一格式混合使用。
3. 大小写敏感的跨平台注意事项
官方文档特别提醒(见 docs/pyproject.md):路径是否大小写敏感遵循平台默认行为,但建议保持大小写一致。例如在 macOS/Windows 上可以写readme = "rEaDmE.mD"匹配README.md,但 Linux 用户克隆仓库后执行poetry install会因大小写敏感而失败。多 README 场景下,务必确保配置中的路径与磁盘上的实际文件名大小写完全一致。
六、最佳实践小结
结合配置文档与仓库实现,使用 Poetry 多 README 功能时建议遵循以下要点:
- 单文件优先用
[project]:只有一个 README 时,写在project.readme;只有需要多文件时才启用dynamic = ["readme"]配合tool.poetry.readme列表; - 路径相对
pyproject.toml:所有路径按隐含规则相对项目根目录解析,用docs/子目录组织多份文档更清晰; - 顺序即展示顺序:多个文件按列表顺序以换行拼接成最终
Description,重要内容放前面; - 提交前跑
poetry check:利用其 README 存在性校验避免打包后才发现文件缺失; - 构建后核验产物:用
poetry build+tar -tzf检查 sdist 内是否同时包含全部 README,参考 tests/console/commands/test_build.py 中的断言方式; - 注意大小写与格式:保持路径大小写一致以保证 Linux 上可安装,并在发布前确认长描述渲染效果。
通过readme列表,Poetry 让「一份文档」的模型平滑扩展为「一组文档」,既保持了 PEP 621 标准的兼容性,又为多语言、多主题的文档组织提供了原生支持。本文涉及的夹具 README-1.rst、README-2.rst 与 pyproject.toml 可直接作为最小可运行示例,配合 docs/pyproject.md 与 docs/basic-usage.md 一起阅读,即可完整掌握这一能力。
【免费下载链接】poetryPython packaging and dependency management made easy项目地址: https://gitcode.com/GitHub_Trending/po/poetry
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考