Python 打包配置现代化:告别 setup.py 混乱,拥抱 pyproject.toml 的丝滑管理

📅 2026/8/12 18:41:21
Python 打包配置现代化:告别 setup.py 混乱,拥抱 pyproject.toml 的丝滑管理
Python 打包配置现代化告别setup.py混乱拥抱pyproject.toml的丝滑管理你的 Python 项目曾经只有一个简洁的setup.py随着依赖增多、构建复杂、工具链碎片化它逐渐膨胀成一个混杂的脚本setuptools、wheel、tox、flake8、pytest的配置散落在setup.cfg、.flake8、pytest.ini、tox.ini各处版本管理、依赖锁定各执一词。新成员加入时光看配置文件就要花半天CI 流水线也因为缺少标准化而频繁踩坑。你隐约听说pyproject.toml是未来但尝试迁移时却发现setuptools不支持、工具配置格式五花八门甚至导致打包失败。Python 打包的现代化远非简单替换一个文件而是一场从理念到工具的全面升级。本文将直击 Python 项目配置现代化的核心疑难为什么需要从setup.py迁移到pyproject.tomlsetup.cfg和pyproject.toml到底是什么关系不同构建后端Setuptools、Poetry、PDM、Flit如何在pyproject.toml中统一表达并提供一份从老项目平滑迁移的实战清单让你的项目配置从此清爽、标准、无痛。一、血泪现场setup.py独大的四大“罪状”1.1 可执行代码的隐患依赖解析时就要执行任意 Pythonsetup.py本质上是一个 Python 脚本安装工具pip在解析依赖时必须先执行它。这意味着哪怕你只想看看这个包需要哪些依赖setup.py中的import numpy或读取requirements.txt的逻辑也会被触发。历史上就曾出现过恶意包的setup.py窃取环境变量的事件。1.2 配置碎片化一个项目数十个配置文件你需要定义包元数据setup.py、设置代码风格.flake8、测试pytest.ini或tox.ini、覆盖率.coveragerc、mypymypy.ini……每个工具都要一个文件根目录一片混乱。想统一管理不存在的。1.3 构建后端与前端分离混乱setuptools是事实标准但poetry、flit、pdm等现代工具各自定义了依赖和构建方式。当你想从setuptools切换到poetry所有打包逻辑都需要重写且poetry生成的包与setuptools并不完全兼容CI 流程都要推倒重来。1.4 无法声明构建依赖setup.py本身依赖setuptools和wheel但如何告诉 pip “安装我这个包之前你需要先安装这些构建依赖”早期只能通过setup_requires参数但它并不可靠容易导致构建失败。直到 PEP 518 引入pyproject.toml才系统性地解决了“构建系统的依赖”问题。二、根因剖析pyproject.toml与setup.cfg的定位与标准演进Python 打包现代化的里程碑是几份 PEPPEP 518 (2016)定义了pyproject.toml文件用于声明构建系统依赖如setuptools、wheel让 pip 知道在构建包之前需要安装什么工具。PEP 621 (2020)将项目元数据名称、版本、依赖等标准化到pyproject.toml的[project]表中让任何构建工具都能用统一格式读取包信息。PEP 517定义了构建后端接口允许使用非 setuptools 的构建系统。在这样的标准下setup.cfg是setuptools的静态配置文件INI 格式用于替代setup.py中的大部分元数据声明但它仍然是setuptools专用的且不能声明构建系统依赖。pyproject.toml是通用项目配置文件可以同时容纳构建系统定义[build-system]项目元数据[project]标准化字段如name,version,dependencies各种工具的配置[tool.pytest.ini_options]、[tool.mypy]、[tool.black]等关键关系你可以把pyproject.toml看成是setup.cfgrequirements.txt 各种工具配置的统一容器而setup.cfg作为过渡方案仍然可以在setuptools项目中与pyproject.toml共存但最终目标是将所有配置迁移进pyproject.toml。三、解决方案一pyproject.toml标准项目元数据配置下面是一个典型的基于Setuptools的现代pyproject.toml示例[build-system] requires [setuptools68.0, wheel] build-backend setuptools.build_meta [project] name my-awesome-app version 1.0.0 description 一个现代化的 Python 项目 readme README.md requires-python 3.9 license {text MIT} authors [ {name Your Name, email youexample.com} ] keywords [sample, packaging] classifiers [ Development Status :: 4 - Beta, Programming Language :: Python :: 3, ] dependencies [ fastapi0.100.0, uvicorn[standard], pydantic2.0.0 ] [project.optional-dependencies] dev [ pytest7.0, black23.0, mypy1.0 ] all [ my-awesome-app[dev], redis4.0 ] [project.urls] Homepage https://example.com Documentation https://readthedocs.org Repository https://github.com/me/my-awesome-app [tool.setuptools] # 如果包不在根目录可指定 # packages {find {where [src]}} [tool.pytest.ini_options] testpaths [tests] addopts -v --tbshort [tool.mypy] python_version 3.9 strict true ignore_missing_imports false [tool.black] line-length 100 target-version [py39] [tool.isort] profile black核心要点[build-system]声明了构建这个包需要的前置依赖setuptools、wheel和构建后端。这是入口没有它 pip 无法构建。[project]下的dependencies就是运行时依赖完全替代了install_requires。optional-dependencies对应extras_require。其他工具配置以[tool.xxx]形式挂载彻底告别分散的 dotfiles。注意如果使用setuptools一些高级特性如 C 扩展、复杂包发现仍需通过setup.py或setup.cfg补充但对于 90% 的纯 Python 项目pyproject.toml已完全足够。四、解决方案二从setup.py/setup.cfg迁移到pyproject.toml4.1 迁移步骤创建pyproject.toml按上述模板填入项目元数据。移动依赖将setup.py中的install_requires和extras_require移到[project]相应字段。删除冗余文件如果setup.cfg只包含 setuptools 配置可将其内容转为[tool.setuptools]后删除一些工具配置也逐一转移。保留最小setup.py如果需要对于包含 C 扩展或动态逻辑的项目可以保留一个简单的setup.py内容仅调用setup()但所有静态数据从pyproject.toml读取。测试构建运行python -m build确保包可以正常构建。更新 CI将构建命令从python setup.py sdist bdist_wheel改为python -m build。4.2setuptools的动态元数据处理如果你的version是从 VCS 标签动态获取的如setuptools_scm可在pyproject.toml中声明[build-system] requires [setuptools64, setuptools_scm8] build-backend setuptools.build_meta [project] dynamic [version] # 不需要写 version 字段 [tool.setuptools_scm] write_to src/my_package/_version.py五、解决方案三不同构建后端的统一配置Poetry、PDM、Flit现代工具纷纷拥抱pyproject.toml作为唯一配置源但它们在[tool]下面的格式仍有差异。5.1 PoetryPoetry 使用[tool.poetry]而不是标准[project]因为 Poetry 在 PEP 621 之前诞生它有自己的格式但最新版也支持 PEP 621 了可混合使用。经典配置[tool.poetry] name myapp version 1.0.0 description authors [Me meexample.com] readme README.md [tool.poetry.dependencies] python ^3.9 fastapi ^0.110.0 [tool.poetry.group.dev.dependencies] pytest ^8.0 [build-system] requires [poetry-core] build-backend poetry.core.masonry.api5.2 PDMPDM 完全遵循 PEP 621使用标准[project]字段同时将自身特有配置放在[tool.pdm]下是迁移成本最低的工具之一。5.3 FlitFlit 以极简著称同样使用标准[project]只需极少配置适合纯 Python 包快速发布。统一趋势随着 PEP 621 的普及所有工具都在向标准[project]表靠拢自定义部分收敛到[tool.tool-name]。选择工具时优先考虑完全支持 PEP 621 的后端。六、常见坑点速查表现象根因解决方法pip install -e .失败提示“build-backend not found”缺少[build-system]或requires中未包含构建后端确保[build-system]完整并已安装wheel打包后缺少包数据文件include-package-data或MANIFEST.in未配置在[tool.setuptools.package-data]中指定或保留MANIFEST.insetup.cfg和pyproject.toml冲突两个文件都有元数据定义删除setup.cfg中的元数据只保留 setuptools 特有配置或全部迁移工具配置不生效工具未支持pyproject.toml或其 key 名错误查阅工具文档确认 key 路径如[tool.pytest.ini_options]而非[tool.pytest]依赖版本解析错误dependencies格式不规范如用了1.0,2.0缺少空格严格遵循 PEP 508 格式或使用pip-compile验证七、最佳实践让项目配置成为团队的“说明书”一项目一pyproject.toml合并所有工具配置根目录清爽。锁定版本pyproject.toml声明抽象依赖具体锁定使用pip-toolsrequirements.txt或 Poetry 的poetry.lock保持可复现。动态版本用setuptools_scm或hatch-vcs避免手动维护版本号。利用pipx/build构建不再使用python setup.py sdist统一为python -m build。CI 中强制配置一致性使用validate-pyproject工具检查pyproject.toml的格式和有效性。为新项目直接使用现代后端Flit 或 PDM告别setup.py的历史包袱。提供devextras让开发环境一键安装所需工具。八、结语告别混乱拥抱标准pyproject.toml不只是格式变化更是 Python 生态标准化的里程碑。它让项目的构建、依赖、配置全都归于一个文件减少了工具间的摩擦也降低了新人的学习曲线。无论你维护的是几万行的老项目还是刚刚mkdir的新项目都值得立即动手将散落的配置收纳进pyproject.toml这座“统一城堡”。用标准化对抗碎片化让你的 Python 工程从此优雅从容。