怎么使用setuptools将Python脚本打包成可安装的wheel包
详细介绍如何使用setuptools和build工具将Python脚本或模块打包为标准的wheel(.whl)二进制分发包,包含项目规范结构、pyproject.toml核心配置、构建操作、安装验证与常见问题排查全流程。
.whl 文件),核心流程是:规范代码结构 → 编写声明元数据与依赖的 pyproject.toml 配置文件 → 安装并调用标准 build 工具执行打包 → 获得预编译归档并在目标环境通过 pip install 一键安装。相比于直接复制单个脚本,wheel 包能自动随包安装第三方依赖,并支持将脚本函数注册为系统全局命令。
一、认识 Wheel 包及其解决的问题
在日常开发中,许多人习惯直接将一个 .py 脚本发给同事或部署到服务器上。但这种方式在实际使用中很容易遇到三个问题:一是脚本依赖的第三方库(如 requests、pandas)需要接收方手动逐个安装,稍有遗漏就会报 ModuleNotFoundError;二是单文件无法直接在系统终端中像内置命令一样随处敲命令运行;三是随着功能增加,多文件与配置难以整洁地管理与版本回滚。
Wheel(PEP 427 标准)是 Python 官方推荐的构建分发格式(Built Distribution)。它的本质是一个经过特别组织的 ZIP 归档,扩展名为 .whl。与传统的源码包(sdist,.tar.gz)不同,wheel 包在安装时不需要重新运行 setup.py 脚本,pip 只需要将其解压缩到目标环境的 site-packages 目录,并为其中的控制台入口创建可执行启动脚本。这使得安装过程安全、干净且速度极快。
二、整理项目目录结构
打包前需要为代码建立清晰的工程目录。目前 Python 打包界普遍推荐 src 布局(src-layout),它能有效避免在项目根目录下测试时因 Python 优先导入当前工作区目录而掩盖打包遗漏的问题。
以编写一个名为 my_tool 的实用命令行工具为例,标准项目目录结构组织如下:
my_project/
├── pyproject.toml # 项目核心构建与打包配置文件
├── README.md # 项目说明文档(会被打包器读取作为包描述)
├── LICENSE # 开源协议或授权声明文件
└── src/
└── my_tool/ # 核心 Python 包目录
├── __init__.py # 包标识文件,可以导出核心函数或版本号
├── core.py # 业务逻辑代码
└── cli.py # 命令行终端入口函数
其中各个脚本的代码职责明确分工:
src/my_tool/__init__.py:声明包的导出内容,保持简洁:# src/my_tool/__init__.py __version__ = "0.1.0"src/my_tool/core.py:负责具体的业务逻辑计算:# src/my_tool/core.py def process_data(text: str) -> str: return text.strip().upper()src/my_tool/cli.py:负责命令行交互与入口调用:# src/my_tool/cli.py import sys from my_tool.core import process_data def main(): if len(sys.argv) > 1: input_text = " ".join(sys.argv[1:]) else: input_text = "hello wheel" print(f"[my-tool] 处理结果: {process_data(input_text)}") if __name__ == "__main__": main()
.dist-info 元数据的映射机制三、编写打包配置文件 pyproject.toml
在现代 Python 打包标准(PEP 517 和 PEP 518)中,官方推荐使用声明式的 pyproject.toml 替代过去的纯代码形式 setup.py。pyproject.toml 结构清晰,能够在不需要执行任意 Python 脚本的情况下直接解析出项目元数据。
在项目根目录创建 pyproject.toml,写入以下完整内容:
[build-system]
# 声明构建当前 wheel 包所需的底层工具链及后端引擎
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-tool"
version = "0.1.0"
description = "一个使用 setuptools 打包的示范工具"
readme = "README.md"
requires-python = ">=3.8"
license = { text = "MIT" }
authors = [
{ name = "Developer", email = "developer@example.com" }
]
# 声明当前脚本运行时所依赖的第三方库(按需填写)
dependencies = [
"requests>=2.28.0"
]
# 关键配置:将 Python 函数注册为终端可直接敲击的全局控制台命令
[project.scripts]
my-cli = "my_tool.cli:main"
[tool.setuptools.packages.find]
# 告知 setuptools 从 src 目录下自动扫描包
where = ["src"]
[build-system]:指明打包构建由setuptools.build_meta接管,这是标准 PEP 517 后端。dependencies:安装该 wheel 包时,pip会自动检查并安装此处列出的第三方库,彻底解决手动安装依赖的繁琐。[project.scripts]:左侧my-cli是你希望用户在终端敲击的命令名,右侧"my_tool.cli:main"指向src/my_tool/cli.py文件中的main()函数。
四、准备构建工具并执行打包
以往很多教程会让用户直接执行 python setup.py bdist_wheel,但在现代标准中,该方式已被官方标记为弃用(Deprecated)。最规范的做法是使用独立的构建驱动器 build(即 PyPA 维护的 pypa/build)。
1. 安装构建依赖
在当前 Python 环境中升级 setuptools、wheel 并安装 build:
# 示例命令:安装与更新打包工具链
pip install --upgrade setuptools wheel build
2. 执行构建命令
在包含 pyproject.toml 的项目根目录下,运行以下构建命令:
# 示例命令:仅构建 wheel 二进制分发包
python -m build --wheel
* Creating isolated environment...
* Installing packages in isolated environment... (setuptools >= 61.0, wheel)
* Getting build dependencies for wheel...
* Building wheel...
Successfully built my_tool-0.1.0-py3-none-any.whl
3. 检查生成产物
构建成功后,项目根目录会新增一个 dist/ 文件夹,里面存放着生成的 .whl 文件:
# 示例输出查看
$ ls dist/
my_tool-0.1.0-py3-none-any.whl
这个单文件就是你可以直接分发给他人、上传至私有包仓库或在生产环境中部署的安装包。
五、本地安装与结果验证
构建完成后,切忌在当前源码开发目录下直接测试,因为当前目录下的 src/ 可能会对 Python 导入产生干扰。推荐新建一个干净的虚拟环境进行验收。
1. 创建并激活全新虚拟环境
# 示例命令:创建临时测试虚拟环境
python -m venv test_env
# 激活虚拟环境(macOS/Linux)
source test_env/bin/activate
# 激活虚拟环境(Windows)
# test_env\Scriptsctivate
2. 安装生成的 Wheel 包
直接使用 pip install 指向刚才生成的 .whl 文件路径:
# 示例命令:安装本地 wheel 包
pip install dist/my_tool-0.1.0-py3-none-any.whl
pip 会自动解包 my_tool 模块到虚拟环境的 site-packages,安装 pyproject.toml 中声明的 requests 依赖,并在虚拟环境的 bin/ 目录下生成名为 my-cli 的可执行脚本。
3. 验证功能与元数据
在终端中直接运行配置好的控制台命令:
# 示例执行
$ my-cli "hello antigravity"
[my-tool] 处理结果: HELLO ANTIGRAVITY
或者通过 Python 解释器导入验证:
$ python -c "import my_tool; print(my_tool.__version__)"
0.1.0
查看包元数据与依赖关系确认安装无误:
$ pip show my-tool
Name: my-tool
Version: 0.1.0
Summary: 一个使用 setuptools 打包的示范工具
Requires: requests
六、常见问题排查与注意事项
初次打包时,开发者往往会遇到一些细节问题。通过以下排查矩阵可以快速定位并修复:
| 故障现象 | 常见原因 | 解决方法 |
|---|---|---|
终端报错 command not found |
[project.scripts] 格式错误或未将安装目录加入环境变量 |
确保写法为 命令名 = "包名.模块名:函数名"(无括号)。若未使用虚拟环境,确认 ~/.local/bin 在系统 PATH 中。 |
用户运行报 ModuleNotFoundError |
未在 dependencies 中声明依赖,误以为本地已有即全局已有 |
在 pyproject.toml 的 dependencies = [...] 中补齐所有外部依赖库及最低版本要求。 |
| 打包后缺少子模块或文件夹 | 子文件夹内缺少 __init__.py,导致 setuptools 未将其识别为有效 Python 包 |
在每个包含 Python 文件的子目录中放置一个 __init__.py(即使为空文件)。 |
| 静态配置文件(.json/.yaml)未打入 wheel | 默认情况下打包器只收集 .py 文件 |
在 pyproject.toml 中增加配置:[tool.setuptools.package-data] |
七、打包规范与最佳实践总结
使用 setuptools 打包 Python 脚本并非单纯为了生成一个文件,而是建立标准工程化协作的基础。最后,我们梳理出纯 Python 项目打包的四条关键准则:
- 拥抱声明式标准配置:优先采用
pyproject.toml管理项目元数据与构建依赖,摆脱对复杂脚本式setup.py的过度依赖。 - 坚持使用
src目录隔离:让开发源码与构建输出环境严格隔离,彻底杜绝本地路径污染导入路径的问题。 - 使用
python -m build统一构建:利用隔离沙箱机制自动下载构建依赖,避免本地机器环境污染生成的 wheel 包。 - 发布前在干净虚拟环境中完成验证:通过
pip install dist/*.whl全流程跑通安装、依赖拉取和 CLI 命令运行,确保下游用户拿到即用。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













