首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python打包指南:setup.py 与 pyproject.toml 的全面对比与实战

Python打包指南:setup.py 与 pyproject.toml 的全面对比与实战

原创
作者头像
用户11541864
发布2025-08-16 15:09:51
发布2025-08-16 15:09:51
8830
举报

Python项目打包时,你是不是也纠结过:该用传统的setup.py ,还是新潮的pyproject.toml ?前者是“老江湖”,配置灵活但代码冗余;后者是“新规范”,简洁清晰却兼容性存疑。本文通过一个真实项目(打包一个命令行工具),从配置复杂度、功能覆盖、工具兼容性三个维度,手把手教你“什么时候用setup.py ,什么时候必须上pyproject.toml ”,让你不再被“打包配置”劝退。

一、核心差异:从“代码配置”到“声明式文件”,打包逻辑大不同

1. setup.py :像“写脚本”,适合复杂逻辑

setup.py 本质是可执行Python脚本,通过调用setuptools的setup()函数定义打包信息:

代码语言:javascript
复制
python复制from setuptools import setup, find_packages  
 
setup(  
    name="mycli",  
    version="1.0.0",  
    packages=find_packages(exclude=["tests*"]),  
    entry_points={  
        "console_scripts": ["mycli=mycli.main:run"]   
    },  
    install_requires=["requests>=2.25.0"]  
)  

优点:能写条件判断(比如根据Python版本选依赖)、读取文件内容(比如从README.md 读取long_description),甚至调用外部命令生成版本号——适合“需要动态配置”的复杂项目。 缺点:代码冗余(每次都要import setup),容易写错(比如漏写packages参数导致模块没打包),且无法被现代打包工具(如pip 21.3+)完全识别。

2. pyproject.toml :像“填表单”,简洁且标准化

pyproject.toml 是声明式配置文件,遵循PEP 621标准,用键值对定义项目信息,无需写Python代码:

代码语言:javascript
复制
toml复制[project]  
name = "mycli"  
version = "1.0.0"  
dependencies = ["requests>=2.25.0"]  
readme = "README.md"   
 
[project.scripts]  
mycli = "mycli.main:run"   
 
[tool.setuptools.packages.find]  
exclude = ["tests*"]  

优点:结构清晰(配置项分类明确)、工具中立(支持setuptools、poetry等多种后端)、不易出错(格式有严格校验)——2023年后新建项目的“官方推荐写法”。 缺点:不支持动态逻辑(比如无法在配置里写if-else),部分老工具(如setuptools<42.0)不兼容。

二、实战对比:用两种配置打包同一个项目,差距在哪里?

1. 基础信息配置:pyproject.toml 更“一目了然”

以“定义项目名称、版本、依赖”为例,两者的对比像“手机拍照”vs“单反手动调参数”:

  • setup.py :需要手动import find_packages,依赖写在install_requires列表里,entry_points要嵌套字典,新手容易漏写逗号导致语法错误;
  • pyproject.toml :[project]下直接写name/version,dependencies是数组,scripts用“命令=模块路径”的简单映射——配置项“见名知意”,复制粘贴就能用。
2. 复杂场景:setup.py 的“动态能力”无可替代

如果你的项目需要根据环境切换配置(比如Windows和Linux依赖不同包),setup.py 的优势就体现出来了:

代码语言:javascript
复制
python复制# setup.py 动态依赖示例  
import sys  
 
setup(  
    # ...其他配置  
    install_requires=[  
        "requests>=2.25.0",  
        "pywin32>=300; sys_platform == 'win32'"  # 仅Windows需要  
    ]  
)  

而pyproject.toml 目前不支持条件依赖(需配合setup.cfg 或工具扩展),这种场景下只能“pyproject.toml+setup.py 混合使用”——前者定义基础信息,后者处理动态逻辑。

3. 工具兼容性:pip/poetry/pipx支持情况

工具

setup.py 支持度

pyproject.toml 支持度(PEP 621)

pip 20.3+

完全支持

完全支持(需setuptools>=42.0)

poetry 1.2+

支持(作为兼容模式)

原生支持(推荐写法)

pipx(安装工具)

支持

支持(需项目有pyproject.toml )

setuptools 58+

支持

支持(需配合[project]表)

结论:2023年后的环境(pip>=21.3+setuptools>=61.0)用pyproject.toml 完全没问题;如果需要兼容Python 3.6以下或老工具,建议保留setup.py 。

三、怎么选?3个场景告诉你答案

1. 新项目首选pyproject.toml :简洁、标准、未来兼容

如果你在2024年新建项目,直接用pyproject.toml+setuptools :

  • 步骤:项目根目录新建pyproject.toml ,按上述示例填写[project]和[tool.setuptools]配置,无需setup.py ;
  • 打包命令python -m build(需安装build工具:pip install build),自动生成wheel和sdist包,比python setup.py sdist bdist_wheel更规范。
2. 老项目迁移:先加pyproject.toml ,再逐步淘汰setup.py

已有setup.py 的项目不用“一刀切”删除,可分两步迁移:

  1. 新建pyproject.toml ,复制setup()函数里的name/version/dependencies等“静态配置”到[project]表;
  2. setup.py 只保留动态逻辑(如条件依赖、生成版本号),并在pyproject.toml 中声明build后端:
代码语言:javascript
复制
toml复制[build-system]  
requires = ["setuptools>=61.0", "wheel"]  
build-backend = "setuptools.build_meta"   

这样工具会优先读取pyproject.toml ,setup.py 仅作为“动态配置补充”。

3. 动态配置场景:pyproject.toml+setup.py 混合使用

如果需要动态逻辑(如从文件读取版本号),可在setup.py 中导入pyproject.toml 的配置,再叠加动态内容:

代码语言:javascript
复制
python复制# setup.py 示例(混合模式)  
from setuptools import setup  
import tomllib  
 
with open("pyproject.toml",  "rb") as f:  
    pyproject = tomllib.load(f)   
 
setup(  
    **pyproject["project"],  # 导入pyproject.toml 中的静态配置  
    version=generate_version(),**动态生成版本号  
)  

四、避坑指南:新手常犯的3个错误

1. pyproject.toml 中漏写build-system配置

如果打包时提示“ERROR: Could not find a valid build backend”,大概率是没加[build-system]段——这是告诉工具“用setuptools打包”,必须写:

代码语言:javascript
复制
toml复制[build-system]  
requires = ["setuptools>=61.","wheel"]  # 至少包含这两个依赖  
build-backend = "setuptools.build_meta"   
2.** setup.py 中用find_packages漏掉模块**

传统setup.py 如果没写packages=find_packages(),会导致子模块没被打包——pyproject.toml 通过[tool.setuptools.packages.find]配置,更不容易出错3. 依赖版本写太死导致安装失败** 无论是setup.py 的install_requires还是pyproject.toml** 的dependencies,依赖版本尽量用>=而非固定版本(如requests==**2.25.0),避免用户环境冲突——这是打包配置“通用铁律”。**

五、写在最后:没有“银弹”配置,只有“合适”选择setup.py 像“瑞士军刀”,功能全面但需要手动开合;pyproject.toml 像“胶囊药”,标准化封装但灵活性受限。新项目直接上pyproject.toml ,享受规范带来的简洁;老项目或复杂项目保留setup.py ,用动态能力解决实际问题——两者不是“非此即彼”,而是“各取所长”。

记住**:打包的核心目标是“让用户能用pip install顺利安装”,配置文件只是手段。与其纠结“用哪个文件”**,不如先写一个最小配置跑通流程——毕竟,能发布到PyPI的项目,比“配置文件格式正确”重要100倍。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、核心差异:从“代码配置”到“声明式文件”,打包逻辑大不同
    • 1. setup.py :像“写脚本”,适合复杂逻辑
    • 2. pyproject.toml :像“填表单”,简洁且标准化
  • 二、实战对比:用两种配置打包同一个项目,差距在哪里?
    • 1. 基础信息配置:pyproject.toml 更“一目了然”
    • 2. 复杂场景:setup.py 的“动态能力”无可替代
    • 3. 工具兼容性:pip/poetry/pipx支持情况
  • 三、怎么选?3个场景告诉你答案
    • 1. 新项目首选pyproject.toml :简洁、标准、未来兼容
    • 2. 老项目迁移:先加pyproject.toml ,再逐步淘汰setup.py
    • 3. 动态配置场景:pyproject.toml+setup.py 混合使用
  • 四、避坑指南:新手常犯的3个错误
    • 1. pyproject.toml 中漏写build-system配置
    • 2.** setup.py 中用find_packages漏掉模块**
  • 五、写在最后:没有“银弹”配置,只有“合适”选择setup.py 像“瑞士军刀”,功能全面但需要手动开合;pyproject.toml 像“胶囊药”,标准化封装但灵活性受限。新项目直接上pyproject.toml ,享受规范带来的简洁;老项目或复杂项目保留setup.py ,用动态能力解决实际问题——两者不是“非此即彼”,而是“各取所长”。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档