4、包发布流程:从源码到包的构建流程、使用setuptools打包Python项目、发布到PyPI与私有仓库

好,咱们聊聊包发布这件事。

说实话,很多SDK开发者写代码是一把好手,但一到「把代码变成包」这一步就卡壳了。我早期也踩过这个坑——代码写好了,同事却不知道怎么装,或者装完跑不起来。说白了,一个SDK的价值,取决于别人能不能轻松地用上它。

4.1 从源码到包的构建流程

先理清一个概念:源码和包,到底差在哪?

源码是你本地开发时那一堆.py文件。而包,是经过打包、压缩、带上元信息(版本号、依赖、入口点等)的发布产物。用户拿到包,直接pip install就能用,不用关心你项目结构长什么样。

整个流程其实就三步:

  1. 准备阶段:写好setup.py或pyproject.toml,配置好元数据
  2. 构建阶段:用python -m build生成.tar.gz源码包和.whl轮子包
  3. 发布阶段:用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 知识体系总览

下面这张图,把整个包发布流程串起来了。我建议你保存下来,每次发布前对照着走一遍。

包发布流程知识体系 阶段一:准备 • 编写 setup.py • 配置 pyproject.toml • 管理依赖版本 阶段二:构建 • python -m build • 生成 .tar.gz • 生成 .whl 阶段三:发布 • twine upload • PyPI / 私有仓库 • 版本号管理 PyPI 公开仓库 全球开发者可安装 私有仓库 公司内部 / 客户专用 最佳实践 ✅ 语义化版本 ✅ 自动化CI/CD ✅ CHANGELOG ✅ 安全认证

这张图把「准备→构建→发布」三个环节串起来了。你每次发布前,可以对照着检查:准备阶段有没有漏掉依赖?构建阶段有没有生成正确的包?发布阶段有没有选对仓库?

嗯,关于包发布,今天就聊这么多。记住一句话:好的SDK,不仅要写得好,还要发得好。下次你发布包的时候,试试我上面说的那些技巧,尤其是setuptools-scm自动版本号,真的能省不少事。

一句话总结:从源码到包,核心就是「准备元数据 → 构建产物 → 上传仓库」三步。每一步都有坑,但用对工具和流程,完全可以自动化、零失误。

可可精品资料库