告别setup.py:用pyproject.toml重塑你的Python项目工作流

张开发
2026/5/7 13:30:44 15 分钟阅读

分享文章

告别setup.py:用pyproject.toml重塑你的Python项目工作流
1. 为什么Python开发者需要告别setup.py十年前我刚接触Python打包时setup.py几乎是每个项目的标配。那时候为了添加一个依赖项我们需要在setup.py里写一堆install_requires然后在命令行执行python setup.py install。现在回想起来这种工作流简直像在用石器时代的工具——不仅繁琐还存在安全隐患。传统配置方式最让我头疼的是它的分散性。一个项目里可能同时存在setup.py、setup.cfg、requirements.txt、MANIFEST.in等多个配置文件。上周我接手一个老项目时光是搞清楚这些文件之间的关系就花了大半天时间。更糟的是setup.py作为可执行脚本可能包含任意代码这在安全性至上的今天已经不合时宜。pyproject.toml的出现彻底改变了这个局面。它采用TOML这种对人类友好的配置格式把所有项目配置集中在一个文件里。我最近将团队内部工具迁移到pyproject.toml后新成员上手时间缩短了60%因为再也不用在多个文件间跳来跳去找配置了。2. 从零开始配置pyproject.toml2.1 基础结构搭建让我们从一个真实案例开始。假设我们要开发一个叫text-cleaner的文本处理工具首先创建项目结构text-cleaner/ ├── src/ │ └── text_cleaner/ │ ├── __init__.py │ └── cleaner.py ├── tests/ └── pyproject.toml最基本的pyproject.toml需要包含两个核心部分[build-system] requires [setuptools61.0] build-backend setuptools.build_meta [project] name text-cleaner version 0.1.0 description Advanced text cleaning utility这里有个实用技巧我习惯在开发初期把版本号设为0.1.0遵循语义化版本规范。当项目进入稳定阶段再升级到1.0.0。2.2 依赖管理进阶传统requirements.txt最大的痛点在于开发依赖和运行依赖混在一起。pyproject.toml通过optional-dependencies完美解决了这个问题[project] dependencies [ numpy1.21.0, pandas1.3.0 ] [project.optional-dependencies] dev [ pytest7.0, black23.0 ] test [ pytest-cov4.0 ]安装时可以使用pip的扩展语法# 安装核心依赖 pip install . # 安装开发环境全套工具 pip install .[dev,test]3. 迁移实战老项目改造指南3.1 自动化迁移工具对于已有项目我强烈推荐使用ini2toml这个神器。上周我把一个包含200依赖的老项目迁移到pyproject.toml整个过程只用了15分钟pip install ini2toml[full] ini2toml setup.cfg pyproject.toml不过要注意自动转换后需要手动检查几个关键点动态版本号处理数据文件包含规则自定义构建步骤3.2 动态内容处理技巧老项目中常见的动态版本号可以通过这种方式迁移[project] dynamic [version] [tool.setuptools.dynamic] version {attr text_cleaner.__version__}对于README文件我推荐这种声明方式readme {file [README.md, CHANGELOG.md]}4. 高级配置与工具集成4.1 现代工具链配置pyproject.toml最让我欣赏的一点是它能统一管理各种开发工具。这是我的标准配置模板[tool.black] line-length 88 target-version [py38] [tool.ruff] select [E, F, W, I] ignore [E501] [tool.pytest.ini_options] minversion 7.0 addopts -v --covsrc testpaths [tests]4.2 多环境构建策略对于需要支持多Python版本的项目可以这样配置[project] requires-python 3.8 classifiers [ Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10 ]我最近遇到一个需要条件依赖的场景解决方案很优雅dependencies [ fastapi0.85.0; python_version3.7, uvicorn0.19.0; python_version3.8 ]5. 打包分发的最佳实践5.1 现代构建流程告别python setup.py sdist bdist_wheel吧现在推荐使用build工具python -m build这个命令会在dist目录生成两种包格式.tar.gz源码包.whl二进制包5.2 发布到PyPI的技巧我习惯先用TestPyPI验证打包结果twine upload --repository testpypi dist/*确认无误后再发布到正式PyPI。这里有个小技巧在pyproject.toml中添加项目链接可以提升PyPI页面专业度[project.urls] Homepage https://github.com/your/text-cleaner Documentation https://text-cleaner.readthedocs.io6. 常见问题解决方案6.1 数据文件包含问题处理非Python文件时老项目常用MANIFEST.in。现在可以直接在pyproject.toml中声明[tool.setuptools.package-data] text_cleaner [data/*.json, templates/*.html]6.2 多入口点管理对于提供多个命令行工具的项目可以这样配置[project.scripts] clean text_cleaner.cli:main_clean analyze text_cleaner.cli:main_analyze [project.gui-scripts] clean-gui text_cleaner.gui:main7. 迁移后的持续优化完成基础迁移后我建议进一步优化项目结构。采用src-layout可以避免很多隐式导入问题project/ ├── src/ │ └── package/ ├── tests/ └── pyproject.toml对于版本管理我创建了这样的自动化流程[tool.bumpversion] current_version 0.1.0配合GitHub Actions每次发布新版本时自动更新版本号并打tag。8. 生态系统兼容性考量虽然pyproject.toml已经成为现代Python项目的标准但在与企业内部系统集成时可能会遇到兼容性问题。我的经验是对于必须使用旧系统的场景可以同时保留setup.py作为过渡与CI/CD系统集成时确保构建环境使用pip21.3文档中明确说明项目采用PEP 517构建标准最近我将Django项目迁移到pyproject.toml后部署流程从原来的8个步骤简化到3个构建时间缩短了40%。这让我深刻体会到标准化配置的价值。

更多文章