Skip to content

在 setuptools 项目的 pyproject.toml 中引用 requirements.txt

问题描述

当迁移基于 setuptools 的 Python 项目到现代 pyproject.toml 配置时,需要保留 pip-compile 工作流程的优点:

  • requirements.txt 提供完整的依赖层级固定,确保 100% 可重复安装
  • ❗ 透明显示依赖冲突,易于调试传递依赖关系

核心问题:
是否能在不依赖传统 setup.py 的情况下,直接在 pyproject.toml 中引用 requirements.txt

使用场景限制

此方案适用于非库项目(如应用程序),对于开源库建议采用宽松依赖声明

解决方案

✅ 推荐方案:使用 setuptools 的动态元数据(Dynamic Metadata)

要求 setuptools ≥ 62.6 版本

基础配置

pyproject.toml 中添加以下配置:

toml
[project]
name = "your-project"
version = "1.0.0"
dynamic = ["dependencies"]

[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }

包含开发依赖

如需添加开发依赖(如 requirements-dev.txt):

toml
[project]
dynamic = ["dependencies", "optional-dependencies"]

[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies = { 
    dev = { file = ["requirements-dev.txt"] } 
}

版本注意事项

txt
首次引入动态依赖文件支持
txt
自动将引用文件包含在 sdist 包中

重要限制

  1. requirements.txt 每行必须符合 PEP 508 规范
  2. 不支持 以下指令: ❌ -r / --requirement
    -c / --constraint
    -e / --editable
  3. 动态配置 optional-dependencies 时,必须全部动态声明

工作原理

通过 setuptools.dynamic 实现声明式依赖加载:

  1. 动态元数据注册
    使用 dynamic = ["dependencies"] 声明依赖将动态加载

  2. 文件路径指定
    tool.setuptools.dynamic.dependencies.file 定义目标文件路径

  3. 构建阶段处理
    setuptools 在构建包时自动读取文件内容并生成元数据

最佳实践

维护工作流示例

  1. requirements.in 中声明基础依赖
  2. 通过 pip-compile 生成 requirements.txt
    bash
    pip-compile requirements.in > requirements.txt
  3. 提交固定后的 requirements.txt

兼容性测试矩阵

setuptools 版本支持特性文件包含方式
< 62.6❌️ 不支持不可用
62.6 - 66.0.x✅ 基本支持需手动配置 MANIFEST.in
≥ 66.1.0✅ 完整支持 + 自动文件包含完全自动

替代方案参考

  1. Hatch 构建系统(如需切换工具链):
    toml
    [build-system]
    requires = ["hatchling", "hatch-requirements-txt"]
    
    [tool.hatch.metadata.hooks.requirements_txt]
    files = ["requirements.txt"]
  2. uv 工具链(依赖管理):
    bash
    uv pip install -r requirements.txt

常见问题

如何检查文件合规性?

bash
# 示例检查(非官方工具)
grep -vE '^#|^$' requirements.txt | grep -Pv '^\s*\w+([=<>]=|;)\S+'

无输出则表示符合规范

安装前如何验证?

bash
# 测试构建过程
python -m build --no-isolation --sdist