|
|
这几年给开源项目打包发版,最容易踩坑的不是代码本身,而是“怎么优雅地把它送上 PyPI”。我把自己的常用流程和一些反复验证的做法整理出来,供你对照落地。
先说项目结构。无论用 setuptools、hatchling 还是 poetry,清晰的包布局是第一步。常见有两种:src 布局(推荐)和传统布局。src 布局把包代码放在 src/your_package 下,能有效避免“本地可运行但打包后导入失败”的老坑。测试放在 tests,根目录保留最少文件:pyproject.toml、README、LICENSE、.gitignore、以及可选的CHANGELOG。现在基本都建议用 PEP 621 的 pyproject.toml 统一元数据和构建后端,不必再写 setup.py;连 setup.cfg 也可以逐步退场。
元数据里,name、version、description、authors、license、readme、python 版本要求、classifiers 这些都要填全。readme 直接指向 README.md,并指定 content-type,避免 PyPI 渲染花屏。版本管理建议用语义化版本号,搭配工具自动从 git tag 生成(如 hatch version、poetry version 或 setuptools_scm),减少手改失误。依赖声明分三类:运行依赖、可选依赖(extras)、开发依赖(dev);运行依赖写进项目元数据里,开发依赖交给锁定工具或 requirements-dev.txt,不要混在一起。
构建工具的选择见仁见智。轻量派用 setuptools + build;一体化更省心就选 hatch 或 poetry。无论哪种,都用 python -m build 产出标准的 sdist 和 wheel,这步会帮你提前发现 MANIFEST、包发现、数据文件丢失之类的问题。如果需要捆带非代码资源,记得在配置里启用包数据包含规则,否则上传后用户拿到的包是“空壳”。
发布前要做三件事:单元测试、静态检查、环境验证。单元测试不赘述;静态检查至少要跑 ruff/flake8 和 mypy/pyright,起码把明显的类型和风格坑拦住。环境验证是许多人忽视的关键步骤:创建一个干净虚拟环境,用 pip install dist/your_pkg‑x.y.z‑py3‑none‑any.whl 安装,然后在该环境里 import 和核心调用一遍;这能暴露出“隐式依赖”“入口脚本断裂”“包数据缺失”的问题。需要命令行工具的,检查 console_scripts 入口是否正确生成。
账号与安全层面,PyPI 正式和测试仓库要分开使用。先在 TestPyPI(pypi.org 有入口说明,也可直接访问 https://test.pypi.org/)跑通完整上传与安装链路:twine upload -r testpypi dist/*,再 pip install -i https://test.pypi.org/simple your-package 测试一遍。确认无误再上传正式 PyPI。强烈建议开启 PyPI 的 2FA,并尽量使用“项目 API token”而不是密码;在 CI 中通过环境变量注入,避免把凭据写进配置文件。
CI/CD 可以让版本发布稳定可重复。常见做法是:在 main 分支打 tag 触发工作流,工作流里执行构建、测试、上传到 TestPyPI,人工或语义化检查通过后再上传正式 PyPI。GitHub Actions 搭配 pypa/gh-action-pypi-publish 非常顺手;多 Python 版本矩阵测试能及早发现兼容性问题。别忘了缓存构建依赖、启用最低支持 Python 版本的“下限依赖测试”,这样才能保证安装边界条件不爆雷。
文档和可发现性也影响下载量。README 里放上简明的安装命令、最短运行示例、兼容矩阵、变更记录链接,用户三十秒内能上手是最好的体验。项目主页、问题跟踪、文档网站在元数据里都能填,PyPI 页面会自动展示,降低沟通成本。如果项目依赖原生扩展或系统库,务必在 README 的“安装”章节直说,附上平台差异和解决方案链接,省去无谓的 issue 往返。
最后的易错点清单:忘记更新版本号导致 400;sdist 缺文件、wheel 缺包数据;入口脚本名与包名不一致;依赖上界全放开导致破坏性升级;私有导入路径在本地可用、安装后失效;README 渲染失败;没有在干净环境验证安装。把这些前置到本地和 CI 里逐一勾掉,发版会变成一件“可预期”的小事。发布不是终点,维护一个健康的发行节奏和清晰的变更记录,才是与用户长期相处的诀窍。 |
|