1. 为什么我们需要告别setup.py如果你用过Python开发项目肯定对setup.py这个文件不陌生。这个文件就像是Python项目的身份证里面定义了项目的名称、版本、依赖等信息。但说实话每次写setup.py都让我头疼不已 - 那些复杂的参数、难以调试的语法还有各种莫名其妙的错误。我最近接手一个老项目setup.py文件足足有200多行。光是理解里面的各种配置就花了我大半天时间。更糟的是这个文件里还混入了不少业务逻辑代码导致打包过程经常失败。这让我下定决心要寻找更好的解决方案。好消息是Python社区也意识到了这个问题。随着PEP 517和PEP 621标准的推出pyproject.toml正在成为新的打包标准。这个文件采用TOML格式比Python代码更易读、更结构化。我最近把所有项目都迁移到了pyproject.toml打包过程变得简单多了。2. pyproject.toml与传统setup.py的对比2.1 格式差异setup.py是一个Python脚本里面调用setuptools.setup()函数来定义项目信息。这种方式虽然灵活但也带来了不少问题from setuptools import setup setup( namemy_project, version0.1, packagesfind_packages(), install_requires[ requests2.25.0, numpy1.20.0 ], # 还有几十个其他参数... )而pyproject.toml采用TOML格式结构更加清晰[project] name my_project version 0.1 dependencies [ requests2.25.0, numpy1.20.0 ]2.2 功能对比pyproject.toml不仅替代了setup.py的功能还带来了更多优势声明式配置不再需要执行Python代码减少了出错可能统一标准所有工具都使用同一个配置文件更丰富的元数据支持更多项目信息的定义构建系统隔离明确指定构建依赖避免污染环境我在迁移过程中发现pyproject.toml的另一个巨大优势是工具兼容性。现在主流的Python工具如pip、build、poetry等都支持pyproject.toml这意味着你不再需要为不同工具维护多个配置文件。3. 如何创建pyproject.toml文件3.1 基本结构一个典型的pyproject.toml文件包含以下几个部分[build-system] requires [setuptools61.0] build-backend setuptools.build_meta [project] name my_project version 0.1.0 authors [ {name John Doe, email johnexample.com} ] description A small example package readme README.md requires-python 3.7 classifiers [ Programming Language :: Python :: 3, License :: OSI Approved :: MIT License, Operating System :: OS Independent, ] [project.urls] Homepage https://example.com Bug_Tracker https://example.com/issues3.2 关键配置详解build-system部分requires指定构建依赖通常是setuptools和wheelbuild-backend指定构建后端setuptools是最常用的选择project部分name项目名称在PyPI上必须唯一version项目版本建议遵循语义化版本规范dependencies项目运行时依赖optional-dependencies可选依赖可以分组定义我建议至少包含这些基本信息这样你的包才能正确发布到PyPI。对于更复杂的项目你还可以添加scripts命令行工具入口点package-data非Python文件资源entry-points插件系统集成点4. 从零开始构建Python包4.1 项目结构让我们通过一个实际例子来演示如何用pyproject.toml打包Python项目。假设我们要打包一个简单的数学工具库math_utils/ ├── src/ │ └── math_utils/ │ ├── __init__.py │ ├── basic.py │ └── advanced.py ├── tests/ │ └── test_math_utils.py ├── pyproject.toml ├── README.md └── LICENSE4.2 编写pyproject.toml[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta [project] name math-utils version 0.1.0 authors [ {name Jane Smith, email janeexample.com} ] description A collection of useful math functions readme README.md requires-python 3.8 license {text MIT} classifiers [ Development Status :: 3 - Alpha, Intended Audience :: Developers, Programming Language :: Python :: 3, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10, ] [project.urls] Homepage https://github.com/username/math-utils Documentation https://github.com/username/math-utils#readme Bug_Tracker https://github.com/username/math-utils/issues [tool.setuptools] package-dir { src}4.3 构建和安装现在我们可以构建并安装这个包了# 安装构建工具 pip install build # 构建项目 python -m build # 安装生成的wheel文件 pip install dist/math_utils-0.1.0-py3-none-any.whl构建完成后你会在dist目录下看到两个文件.tar.gz源码分发.whl二进制分发5. 高级配置技巧5.1 处理依赖关系pyproject.toml可以非常灵活地管理依赖[project] dependencies [ requests2.25.0, numpy1.20.0; python_version3.8, pandas1.3.0; python_version3.8 and python_version3.11 ] [project.optional-dependencies] test [ pytest6.0, pytest-cov2.0 ] dev [ black, flake8, mypy ]安装可选依赖组pip install math-utils[test,dev]5.2 包含非Python文件如果你需要包含数据文件或文档可以这样配置[tool.setuptools] include-package-data true [tool.setuptools.package-data] math_utils [data/*.json, templates/*.html]5.3 定义命令行工具[project.scripts] math-cli math_utils.cli:main [project.gui-scripts] math-gui math_utils.gui:main这会在安装包时创建math-cli和math-gui命令。6. 常见问题与解决方案在迁移到pyproject.toml的过程中我遇到过不少问题这里分享几个典型的问题1构建时报错invalid project name原因项目名称不符合PyPI命名规范解决只使用字母、数字和连字符且不能以数字开头问题2安装后无法导入包原因通常是因为包目录结构不正确解决确保在pyproject.toml中正确配置了package-dir问题3依赖项安装失败原因依赖声明格式错误解决检查dependencies中的包名和版本说明符格式问题4构建速度慢原因每次构建都重新安装构建依赖解决使用build的--isolated参数或预先安装构建依赖7. 迁移现有项目的最佳实践如果你有一个使用setup.py的老项目可以按照以下步骤迁移备份现有配置复制setup.py中的重要配置创建pyproject.toml将关键信息转换为TOML格式测试构建确保能正确构建wheel文件验证安装安装生成的wheel并测试功能更新文档修改README等文档中的构建说明删除setup.py确认一切正常后可以删除旧文件我最近将一个中型项目从setup.py迁移到pyproject.toml整个过程大约花了2小时。最大的挑战是处理一些复杂的自定义构建逻辑但最终结果非常值得 - 构建时间缩短了30%配置也更加清晰易维护。8. 现代Python打包生态pyproject.toml的出现改变了Python打包的生态系统。现在除了setuptools你还可以选择其他构建后端Poetry专注于依赖管理和发布流程Flit适合简单项目的轻量级工具Hatch提供完整的项目生命周期管理PDM支持PEP 582的现代工具这些工具都使用pyproject.toml作为配置文件但提供了不同的功能和用户体验。我建议根据项目需求选择合适的工具 - 对于大多数库项目setuptoolspyproject.toml的组合已经足够好用了。
告别setup.py:使用pyproject.toml打造现代Python包
发布时间:2026/7/15 4:28:00
1. 为什么我们需要告别setup.py如果你用过Python开发项目肯定对setup.py这个文件不陌生。这个文件就像是Python项目的身份证里面定义了项目的名称、版本、依赖等信息。但说实话每次写setup.py都让我头疼不已 - 那些复杂的参数、难以调试的语法还有各种莫名其妙的错误。我最近接手一个老项目setup.py文件足足有200多行。光是理解里面的各种配置就花了我大半天时间。更糟的是这个文件里还混入了不少业务逻辑代码导致打包过程经常失败。这让我下定决心要寻找更好的解决方案。好消息是Python社区也意识到了这个问题。随着PEP 517和PEP 621标准的推出pyproject.toml正在成为新的打包标准。这个文件采用TOML格式比Python代码更易读、更结构化。我最近把所有项目都迁移到了pyproject.toml打包过程变得简单多了。2. pyproject.toml与传统setup.py的对比2.1 格式差异setup.py是一个Python脚本里面调用setuptools.setup()函数来定义项目信息。这种方式虽然灵活但也带来了不少问题from setuptools import setup setup( namemy_project, version0.1, packagesfind_packages(), install_requires[ requests2.25.0, numpy1.20.0 ], # 还有几十个其他参数... )而pyproject.toml采用TOML格式结构更加清晰[project] name my_project version 0.1 dependencies [ requests2.25.0, numpy1.20.0 ]2.2 功能对比pyproject.toml不仅替代了setup.py的功能还带来了更多优势声明式配置不再需要执行Python代码减少了出错可能统一标准所有工具都使用同一个配置文件更丰富的元数据支持更多项目信息的定义构建系统隔离明确指定构建依赖避免污染环境我在迁移过程中发现pyproject.toml的另一个巨大优势是工具兼容性。现在主流的Python工具如pip、build、poetry等都支持pyproject.toml这意味着你不再需要为不同工具维护多个配置文件。3. 如何创建pyproject.toml文件3.1 基本结构一个典型的pyproject.toml文件包含以下几个部分[build-system] requires [setuptools61.0] build-backend setuptools.build_meta [project] name my_project version 0.1.0 authors [ {name John Doe, email johnexample.com} ] description A small example package readme README.md requires-python 3.7 classifiers [ Programming Language :: Python :: 3, License :: OSI Approved :: MIT License, Operating System :: OS Independent, ] [project.urls] Homepage https://example.com Bug_Tracker https://example.com/issues3.2 关键配置详解build-system部分requires指定构建依赖通常是setuptools和wheelbuild-backend指定构建后端setuptools是最常用的选择project部分name项目名称在PyPI上必须唯一version项目版本建议遵循语义化版本规范dependencies项目运行时依赖optional-dependencies可选依赖可以分组定义我建议至少包含这些基本信息这样你的包才能正确发布到PyPI。对于更复杂的项目你还可以添加scripts命令行工具入口点package-data非Python文件资源entry-points插件系统集成点4. 从零开始构建Python包4.1 项目结构让我们通过一个实际例子来演示如何用pyproject.toml打包Python项目。假设我们要打包一个简单的数学工具库math_utils/ ├── src/ │ └── math_utils/ │ ├── __init__.py │ ├── basic.py │ └── advanced.py ├── tests/ │ └── test_math_utils.py ├── pyproject.toml ├── README.md └── LICENSE4.2 编写pyproject.toml[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta [project] name math-utils version 0.1.0 authors [ {name Jane Smith, email janeexample.com} ] description A collection of useful math functions readme README.md requires-python 3.8 license {text MIT} classifiers [ Development Status :: 3 - Alpha, Intended Audience :: Developers, Programming Language :: Python :: 3, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10, ] [project.urls] Homepage https://github.com/username/math-utils Documentation https://github.com/username/math-utils#readme Bug_Tracker https://github.com/username/math-utils/issues [tool.setuptools] package-dir { src}4.3 构建和安装现在我们可以构建并安装这个包了# 安装构建工具 pip install build # 构建项目 python -m build # 安装生成的wheel文件 pip install dist/math_utils-0.1.0-py3-none-any.whl构建完成后你会在dist目录下看到两个文件.tar.gz源码分发.whl二进制分发5. 高级配置技巧5.1 处理依赖关系pyproject.toml可以非常灵活地管理依赖[project] dependencies [ requests2.25.0, numpy1.20.0; python_version3.8, pandas1.3.0; python_version3.8 and python_version3.11 ] [project.optional-dependencies] test [ pytest6.0, pytest-cov2.0 ] dev [ black, flake8, mypy ]安装可选依赖组pip install math-utils[test,dev]5.2 包含非Python文件如果你需要包含数据文件或文档可以这样配置[tool.setuptools] include-package-data true [tool.setuptools.package-data] math_utils [data/*.json, templates/*.html]5.3 定义命令行工具[project.scripts] math-cli math_utils.cli:main [project.gui-scripts] math-gui math_utils.gui:main这会在安装包时创建math-cli和math-gui命令。6. 常见问题与解决方案在迁移到pyproject.toml的过程中我遇到过不少问题这里分享几个典型的问题1构建时报错invalid project name原因项目名称不符合PyPI命名规范解决只使用字母、数字和连字符且不能以数字开头问题2安装后无法导入包原因通常是因为包目录结构不正确解决确保在pyproject.toml中正确配置了package-dir问题3依赖项安装失败原因依赖声明格式错误解决检查dependencies中的包名和版本说明符格式问题4构建速度慢原因每次构建都重新安装构建依赖解决使用build的--isolated参数或预先安装构建依赖7. 迁移现有项目的最佳实践如果你有一个使用setup.py的老项目可以按照以下步骤迁移备份现有配置复制setup.py中的重要配置创建pyproject.toml将关键信息转换为TOML格式测试构建确保能正确构建wheel文件验证安装安装生成的wheel并测试功能更新文档修改README等文档中的构建说明删除setup.py确认一切正常后可以删除旧文件我最近将一个中型项目从setup.py迁移到pyproject.toml整个过程大约花了2小时。最大的挑战是处理一些复杂的自定义构建逻辑但最终结果非常值得 - 构建时间缩短了30%配置也更加清晰易维护。8. 现代Python打包生态pyproject.toml的出现改变了Python打包的生态系统。现在除了setuptools你还可以选择其他构建后端Poetry专注于依赖管理和发布流程Flit适合简单项目的轻量级工具Hatch提供完整的项目生命周期管理PDM支持PEP 582的现代工具这些工具都使用pyproject.toml作为配置文件但提供了不同的功能和用户体验。我建议根据项目需求选择合适的工具 - 对于大多数库项目setuptoolspyproject.toml的组合已经足够好用了。