当前位置: 首页 > news >正文

Python 包发布全流程:从项目结构到 PyPI 上线,以及我踩过的那些坑

Python 包发布全流程:从项目结构到 PyPI 上线,以及我踩过的那些坑

本文不深入讲解工具本身,而是以我发布的ratelimited-embedder为例,完整记录一个 Python 包从编写、打包、测试到最终发布到 PyPI 的每一步,并列举常见错误和解决方案,帮你避开那些“坑”。

前言

当你写好一个 Python 工具,想让全世界的人通过pip install your-package轻松安装,就需要把它发布到 PyPI(Python Package Index)。这个过程看似简单,实则有不少容易出错的地方。我在发布第一个包时,就踩了 license 格式、版本号不一致、API token 混淆、换行符警告等一堆坑。

本文将按时间顺序,一步步带你走完整个流程,并附上我当时遇到的错误和解决方法。无论你是初学者还是有一定经验的开发者,相信都能从中受益。


一、准备项目结构

推荐使用src/布局,这是现代 Python 打包的最佳实践,可避免开发时意外导入本地代码而非安装的包。

your-package-name/ ├── src/ │ └── your_package_name/ # 模块名(与包名尽量一致) │ ├── __init__.py │ ├── core.py │ └── ... ├── tests/ # 单元测试 ├── pyproject.toml # 核心配置文件 ├── README.md ├── LICENSE # 许可证(如 MIT, Apache 2.0) └── .gitignore

注意:包名可以使用连字符(如ratelimited-embedder),但导入时的模块名必须使用下划线(ratelimited_embedder)。


二、编写pyproject.toml

这是 Python 打包的“身份证”,必须包含以下核心字段:

[build-system] requires = ["setuptools>=68.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "ratelimited-embedder" # PyPI 上的名称 version = "0.1.1" # 遵循语义化版本 description = "工具简短描述" readme = "README.md" license = { text = "Apache-2.0" } # 注意:不是字符串 "Apache-2.0" authors = [{ name = "Your Name", email = "you@example.com" }] requires-python = ">=3.9" dependencies = [ "psutil>=5.9.0", "tqdm>=4.65.0", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ] [project.urls] Homepage = "https://github.com/yourname/your-repo" Repository = "https://github.com/yourname/your-repo"

踩坑记录

  • ❌ 错误写法:license = "MIT"
    ✅ 正确写法:license = { text = "MIT" }license = { file = "LICENSE" }
  • 缺少许可证分类器:必须在classifiers中添加License :: OSI Approved :: Apache Software License对应的分类器,否则twine check会警告。
  • 版本号不一致pyproject.toml中的version必须与src/your_package/__init__.py中的__version__保持一致。建议使用importlib.metadata动态读取。

三、编写 README 和 LICENSE

  • README.md:应包含安装方法、快速示例、核心 API 说明。PyPI 支持 Markdown 渲染,推荐使用。
  • LICENSE:在 GitHub 创建仓库时可以直接选择许可证模板(MIT、Apache 2.0 等),然后下载到本地。

注意:README 中的示例代码要确保可运行,且与当前版本 API 一致。


四、本地构建与检查

1. 安装构建工具

pipinstallbuild twine

2. 构建分发包

python-mbuild

成功后会生成dist/目录,内含.tar.gz源码包和.whlwheel 包。

3. 检查元数据

twine check dist/*

如果输出PASSED,说明元数据基本正确;如果有警告或错误,需根据提示修改pyproject.toml

常见错误:

  • long_description_content_type缺失或格式不对 → 确保 README 后缀为.md,并在pyproject.toml中不设置(默认会读取)。
  • 缺少许可证分类器 → 添加对应 classifier。

五、在 TestPyPI 上预发布

TestPyPI是 PyPI 的测试实例,在这里上传包不会影响正式环境,非常适合演练。

1. 注册账号并生成 API token

  • 访问 test.pypi.org 注册账号。
  • 登录后进入Account settingsAPI tokensAdd API token
  • 选择 “Entire account”,生成 token(以pypi-开头),复制保存(只显示一次)。

2. 上传到 TestPyPI

twine upload--repositorytestpypi dist/*

输入用户名:__token__
密码:粘贴刚才复制的 token。

3. 验证安装

创建一个干净的环境:

python-mvenv test_envsourcetest_env/bin/activate# Windows: test_env\Scripts\activatepipinstall--index-url https://test.pypi.org/simple/ your-package-name

然后尝试导入,确认功能正常。

踩坑记录

  • Token 搞混:TestPyPI 的 token 不能用于正式 PyPI,反之亦然。上传前务必确认--repository参数。
  • 包名已存在:如果 TestPyPI 上已有同名包(哪怕是别人占用的),需要修改name。一般 TestPyPI 比较宽松,但正式 PyPI 更严格。

六、正式发布到 PyPI

1. 注册正式 PyPI 账号并生成 token

  • 访问 pypi.org 注册(不同于 TestPyPI,账号不通用)。
  • 同样在API tokens中创建一个 token,作用域选 “Entire account”。

2. 上传

twine upload dist/*

用户名:__token__
密码:正式 PyPI 的 token。

上传成功后,访问https://pypi.org/project/your-package-name/就能看到项目页面。

踩坑记录

  • 403 Forbidden:常见原因有:
    • 使用了 TestPyPI 的 token。
    • token 作用域限制为某个项目,而当前项目名不匹配。
    • 包名已被人注册(需要改名)。
  • 版本号重复:PyPI 不允许覆盖已发布的版本。如果需要在同一版本上修复,必须递增版本号(如0.1.10.1.2)。永远不要删除已发布的版本

七、关联 GitHub 与版本标签

为了方便用户查看源码和提 issues,建议将代码同步到 GitHub,并在每次发布时打 tag。

gitadd.gitcommit-m"Release v0.1.1"gittag v0.1.1gitpush origin main--tags

然后在 GitHub 仓库页面创建 Release,附上打包好的dist/*文件(可选)。


八、常见问题汇总

问题原因解决方案
twine upload返回 400pyproject.toml中许可证字段格式错误改为license = { text = "MIT" }
pip install找不到包未上传到正确的 PyPI 仓库检查是否用了--index-url指定 TestPyPI
twine check警告No classifier for licenseclassifiers缺少许可证分类器添加"License :: OSI Approved :: MIT License"
安装后导入报ModuleNotFoundError模块名与包名不一致确保src/下的文件夹名与pyproject.tomlname的连字符转下划线一致
Git 提示LF will be replaced by CRLFWindows 换行符转换警告可以忽略,或设置git config core.autocrlf true
上传时提示no such file or directory: 'dist/*'没有先执行python -m build先构建分发包
用户反馈安装后缺少依赖dependencies中遗漏了必备包列出所有运行时依赖,包括间接依赖(如langchain-core

九、更新包版本

当工具功能更新或修复 bug 时,需要发布新版本:

  1. 修改pyproject.toml中的version,例如0.2.0
  2. 同步修改src/your_package/__init__.py中的__version__
  3. 更新CHANGELOG.md(推荐)。
  4. 重新构建:python -m build
  5. 上传:twine upload dist/*

PyPI 会自动索引新版本,用户执行pip install --upgrade your-package即可升级。


十、总结

发布一个 Python 包并不复杂,但细节繁多。核心步骤可以概括为:

准备项目 → 编写 pyproject.toml → 构建 → TestPyPI 预演 → 正式 PyPI 上传 → GitHub 关联

过程中遇到错误不可怕,关键是要看懂错误信息并善用搜索引擎。希望本文能帮你少走弯路,顺利发布自己的第一个 PyPI 包。

如果觉得有用,欢迎点赞、收藏、转发。有任何问题也可以在评论区交流。

文中示例工具ratelimited-embedder地址:GitHub | PyPI

http://www.jsqmd.com/news/758862/

相关文章:

  • UVM验证实战:AHB SRAMC环境中scoreboard设计、覆盖率收集与结果分析全解析
  • 把FPGA的GTY收发器当成一个“超级串口”:我的自定义协议通信实践(基于KCU116开发板)
  • Unity动画文件太大?别急着改压缩选项,先试试这个文本处理技巧
  • Jaeger数据聚合终极指南:10个技巧实现跨服务性能指标统计与监控
  • DoL-Lyra技术架构深度解析:基于位标志系统的模块化构建引擎
  • 8个实用技巧:轻松解决YuukiPS Launcher启动与运行问题
  • 互联网大厂Java求职面试:从Java SE到微服务的技术深度探讨
  • 5步掌握gofile-downloader:轻松解决Gofile文件下载难题
  • 5分钟快速解密网易云音乐NCM文件:免费开源工具终极指南
  • 告别一堆仪器!用Moku Pro激光锁盒搞定PDH稳频,保姆级配置流程分享
  • CH585的USB-TouchScreen多点触摸参考代码
  • B站CC字幕一键提取:3分钟掌握高效字幕下载与转换技巧
  • 5步掌握roop-unleashed:零基础打造专业级AI换脸视频的终极指南
  • 《QGIS快速入门与应用基础》320:每日任务清单(具体操作项)
  • 毕业了NoteExpress样式只剩7个?别慌,手把手教你用清华版恢复4000+样式(附数据库降级教程)
  • 3大核心技术让d2dx彻底改变你的暗黑破坏神2游戏体验
  • 如何在Firefox中解锁Sketchfab的3D宝藏?一个Tampermonkey脚本的奇妙冒险
  • 你的keystore安全吗?从JKS到PKCS12格式迁移,顺便搞定签名信息提取全流程
  • SAP FICO附件上传踩坑记:从SmartForms生成PDF到关联凭证的完整避坑指南
  • 终极指南:如何构建流畅的Android应用引导页面(AppIntro)
  • Flipper终极指南:如何高效调试Cordova混合应用开发
  • FanControl终极教程:5个步骤掌握Windows风扇智能控制
  • CodeImage最佳实践:如何制作专业级的代码截图?
  • 终极绿色计算方案:如何用diff-match-patch在碳中和时代实现高效文本处理
  • 五粮液:老手死于抄底,先谨慎观察
  • 无人机日志分析终极指南:3分钟掌握免费在线分析工具
  • 四月七日
  • 【Dify权限治理权威白皮书】:基于23家金融/政企客户真实案例验证的7层权限隔离模型
  • 终极指南:如何用Defender Control一键掌控Windows Defender安全防护
  • 终极键盘按键显示工具:让每一次按键都清晰可见的完整指南