4、包发布流程:从源码到包的构建流程、使用setuptools打包Python项目、发布到PyPI与私有仓库
好,咱们聊聊包发布这件事。
说实话,很多SDK开发者写代码是一把好手,但一到「把代码变成包」这一步就卡壳了。我早期也踩过这个坑——代码写好了,同事却不知道怎么装,或者装完跑不起来。说白了,一个SDK的价值,取决于别人能不能轻松地用上它。
4.1 从源码到包的构建流程
先理清一个概念:源码和包,到底差在哪?
源码是你本地开发时那一堆.py文件。而包,是经过打包、压缩、带上元信息(版本号、依赖、入口点等)的发布产物。用户拿到包,直接pip install就能用,不用关心你项目结构长什么样。
整个流程其实就三步:
- 准备阶段:写好
setup.py或pyproject.toml,配置好元数据 - 构建阶段:用
python -m build生成.tar.gz源码包和.whl轮子包 - 发布阶段:用
twine上传到PyPI或私有仓库
嗯,这里要注意:千万别手动改版本号然后直接上传。我见过有人把同一个版本上传了三次,结果用户永远装不到最新版。版本管理一定要自动化。
核心原则:每次发布前,先确认版本号已递增,且CHANGELOG已更新。这是专业SDK团队的基本素养。
4.2 使用setuptools打包Python项目
setuptools是Python打包的「老大哥」。虽然现在有poetry、flit这些新工具,但setuptools依然是兼容性最好的选择。我个人习惯用它,因为几乎所有CI/CD环境都原生支持。
一个典型的setup.py长这样:
from setuptools import setup, find_packages
setup(
name="my-sdk",
version="1.2.0",
author="你的名字",
description="一个示例SDK",
packages=find_packages(exclude=["tests*"]),
install_requires=[
"requests>=2.25.0",
"pydantic>=1.8.0",
],
python_requires=">=3.8",
entry_points={
"console_scripts": [
"my-sdk-cli=my_sdk.cli:main",
],
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
],
)
这里有几个关键点,我展开说说:
find_packages():自动发现所有子包。但记得排除tests目录,不然用户装包时会带上你的测试代码。我在项目中遇到过有人把测试数据也打包进去了,结果包体积大了10倍。install_requires:只写「硬依赖」。那些只在测试或文档生成时才用的库,放到extras_require里。你想想看,用户装个SDK,结果把整个pytest全家桶都装上了,多尴尬。entry_points:如果你SDK提供了命令行工具,这里就是入口。我建议所有SDK都配一个简单的CLI,方便用户快速验证安装是否成功。
小技巧:用python setup.py check可以提前发现元数据错误。我每次打包前都会跑一遍,省得上传后被PyPI拒绝。
4.3 发布到PyPI与私有仓库
发布到PyPI,说白了就是两步:构建 + 上传。但这里面的坑,我踩过不少。
先看构建命令:
# 安装构建工具
pip install build twine
# 构建包
python -m build
# 上传到PyPI(需要先注册账号)
twine upload dist/*
上传时,twine会提示你输入用户名和密码。但别傻乎乎地手动输入——用~/.pypirc文件配置API token,既安全又省事。
[pypi]
username = __token__
password = pypi-xxxxxxxxxxxxxxxxxxxx
我曾经犯过一个低级错误:把__token__写成了自己的用户名,结果认证一直失败。折腾了半小时才发现,嗯,这种低级错误谁还没犯过呢。
私有仓库怎么搞?
很多公司不会把SDK发到公开PyPI,而是用私有仓库。常见方案有:
- GitLab PyPI Registry:如果你用GitLab,它自带包管理功能
- JFrog Artifactory:企业级方案,支持多种包格式
- 简单的HTTP服务器:用
pypiserver或devpi自建
发布到私有仓库的命令几乎一样,只是改一下repository参数:
twine upload --repository-url https://your-private-pypi.com/simple/ dist/*
用户安装时,需要指定额外的index:
pip install my-sdk --extra-index-url https://your-private-pypi.com/simple/
警告:私有仓库的HTTPS证书一定要配好。我见过有人用HTTP,结果包在传输过程中被篡改,整个团队排查了一周才发现是中间人攻击。安全无小事。
4.4 版本管理与发布策略
版本号不是随便写的。我建议严格遵循语义化版本(SemVer):
| 版本格式 | 含义 | 示例 |
|---|---|---|
| 主版本号 | 不兼容的API修改 | 2.0.0 |
| 次版本号 | 向下兼容的功能新增 | 1.3.0 |
| 修订号 | 向下兼容的问题修正 | 1.2.1 |
另外,我强烈建议用setuptools-scm这个工具。它能直接从Git tag自动生成版本号,彻底杜绝「版本号忘记改」的问题。
# setup.py 中配置
setup(
use_scm_version=True,
setup_requires=['setuptools-scm'],
)
然后你只需要打Git tag:
git tag v1.2.0
git push --tags
python -m build # 自动生成版本号 1.2.0
你看,是不是省心多了?
4.5 知识体系总览
下面这张图,把整个包发布流程串起来了。我建议你保存下来,每次发布前对照着走一遍。
这张图把「准备→构建→发布」三个环节串起来了。你每次发布前,可以对照着检查:准备阶段有没有漏掉依赖?构建阶段有没有生成正确的包?发布阶段有没有选对仓库?
嗯,关于包发布,今天就聊这么多。记住一句话:好的SDK,不仅要写得好,还要发得好。下次你发布包的时候,试试我上面说的那些技巧,尤其是setuptools-scm自动版本号,真的能省不少事。
一句话总结:从源码到包,核心就是「准备元数据 → 构建产物 → 上传仓库」三步。每一步都有坑,但用对工具和流程,完全可以自动化、零失误。