pyproject.toml 可完全替代 setup.py,但需显式声明 [build-system] 段指定 build-backend 和 requires,并将元数据移至 [project] 段;动态版本用 dynamic.version 配置,-e 安装依赖后端支持,旧脚本须改为 python -m build。

pyproject.toml 可以完全替代 setup.py,但不是简单“删掉再写个 toml”就完事——它改变了构建流程的控制权归属,必须明确指定构建后端(build backend)并适配其要求。
pyproject.toml 必须声明 build-backend 才能生效
Python 3.11+ 默认启用 PEP 517 构建协议,一旦存在 pyproject.toml,setup.py 就被忽略(即使它还存在)。但若 pyproject.toml 中没有 [build-system] 段,pip install 或 python -m build 会直接报错:
ERROR: No build backend specified in pyproject.toml
正确写法是显式指定后端,最常用的是 setuptools(兼容旧习惯)或 hatchling(更轻量):
[build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta"
-
requires列出构建时依赖,不能写成setuptools简写,需带版本约束(避免因 setuptools 太老导致元数据解析失败) - 若用
hatchling,则写build-backend = "hatchling.build",且requires改为["hatchling"] - 不要混用:比如
requires写setuptools却把build-backend设为hatchling.build,会触发 ImportError
metadata 和 dependencies 要挪到 [project] 下,不是 [tool.setuptools]
很多人误以为 [tool.setuptools] 是新 setup.py 的平替,其实不然。PEP 621 规定标准元数据必须放在 [project] 段,否则 setuptools 构建时不会读取(除非你主动关掉 PEP 621 支持)。
典型迁移对比:
# ❌ 错误:只写在 tool.setuptools 里,会被忽略 [tool.setuptools] name = "mylib" version = "0.1.0" dependencies = ["requests"]
# ✅ 正确:写在 project 下 [project] name = "mylib" version = "0.1.0" dependencies = ["requests>=2.25.0"]
-
dependencies是列表,每个项是 PEP 508 兼容字符串,支持版本约束、环境标记(如"pytest; extra == 'test'") - 可选依赖用
[project.optional-dependencies],不是extras_require -
readme推荐写成readme = "README.md",而非long_description_file
动态 version 或复杂逻辑仍需 setup.py?不,用 pyproject.toml + 钩子
有人卡在“我的版本号从 git tag 读,setup.py 里写了 get_version() 函数,pyproject.toml 怎么办?”——答案是:不用删函数,但要换位置。
setuptools 支持在 [project] 中通过 dynamic.version 委托给 Python 模块:
[project] name = "mylib" dynamic = ["version"] # 其他字段照常写
[project.dynamic.version] attr = "mylib.__version__"
- 这要求
mylib/__init__.py中定义了__version__(可从 git 获取,例如用importlib.metadata.version或setuptools_scm) - 若用
setuptools_scm,需在requires加"setuptools-scm[toml]>=6.2",并在[tool.setuptools_scm]配置(注意:这是工具配置,不影响构建逻辑) - 别试图在 toml 里写 Python 表达式——它不是模板引擎,所有动态行为必须由构建后端支持并实现
本地开发安装时,-e 模式行为有细微差别
执行 pip install -e . 时,传统 setup.py 直接运行 setup.py develop;而 pyproject.toml 下,pip 会调用构建后端的 build_editable 接口。这意味着:
- 如果后端不支持 editable 安装(如某些精简后端),
-e会失败,提示Editable installation is not supported -
setuptools默认支持,但需确保pyproject.toml中build-backend是setuptools.build_meta(不是setuptools.build_meta:__legacy__) - 部分 IDE(如 PyCharm)旧版本可能未识别
pyproject.toml的 editable 安装路径,需手动刷新解释器路径或升级
真正容易被忽略的是:当你删掉 setup.py 后,有些 CI 脚本或 Makefile 还在调用 python setup.py sdist,这些命令会直接报错“No module named setup”——必须统一改为 python -m build --sdist 或 pipx run build --sdist。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











