在维护 PyPI 包和项目的过程中,我常遇到两大痛点:

  1. 上传发布繁琐:过去,上传 PyPI 包通常需要配置 PyPI Token,有一定的安全隐患,同时还需要在本地或 CI 中打包上传,尤其是当涉及 .whl 文件的项目还需要额外的环境配置,工作量会增加不少。
  2. 固定子依赖耗时:稍大型的 Python 项目,子依赖往往较多,不固定版本会有上游库的不兼容风险。传统工具如 PDM、Poetry 等在锁定依赖时速度不尽人意,在开发过程中不得不忍受漫长的耗时甚至固定失败。在 2021 年是我用 Poetry 固定一个项目花了 10 分钟还失败了,自此之后就不用了。

近两年内,这些问题有了舒服的解决方案。

PyPI 支持 Trusted Publishers#

2023 年 PyPI 宣布,支持 Trusted Publishers 的无密码发布机制。这一机制避免了对 PyPI Token 的依赖,只需在 PyPI 后台填写 GitHub Actions 的基本信息,配置过程非常简单,省去许多维护工作。

uv 的发布#

2024 年,uv发布 则彻底改善了 Python 项目固定子依赖的效率问题。之前,PDM、Poetry 等工具虽然好用,但锁定依赖的速度相对较慢,实际项目中持续集成的时间较长。我在 GitHub 上建立了一个对比仓库,用于展示各工具固定我参与过的一个项目的依赖锁定速度(数据基于 Actions 执行时间):

工具耗时
Poetry43s
PDM1m 7s
uv2s

uv 基本上可以在秒级别固定,开发过程几乎无感知的。

更进一步的,则是一些 Python 项目本身依赖机器学习模型,这时候避免不了依赖 PyTorch 及关联的生态项目。这时候会有一些问题:

  • PyTorch 的 tag 即便都是 CPU 版本,在不同的平台也不一样,所以需要根据平台来选择不同的版本。
  • PyTorch 关联的第三方生态项目对其版本要求非常严格,而且有时候遇到安装的版本不对,不会有直观的报错,而是让人摸不到头脑的 Segmentation fault (core dumped)

所以我目前在工作中也将 uv 用于这类项目,通过 uvindex 机制,可以很方便地指定 PyTorch 的源。比如如下是一个使用 uv 的项目的 pyproject.toml 文件:

[project]
name = "foo-bar"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = "==3.12.*"

dependencies = [
    "torch==2.5.0; platform_system == 'Darwin'",
    "torch==2.5.0+cpu; platform_system != 'Darwin'",
]

[[tool.uv.index]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[tool.uv.sources]
torch = { index = "pytorch" }

另外,利用 tool.uv.find-links 配置第三方项目自行发布维护的源,确保安装到正确的版本。这样机器学习相关的项目,可以让本地开发环境(macOS)和线上环境(Linux)尽可能一致(不过涉及比较多的项目细节就不展示了)。这样 Dockerfile 写起来也十分清爽了,不用依赖特定的安装顺序,直接 uv sync --compile --frozen 即可。

进一步的,则是 uv 实际上可以管理 Python 包的构建/打包/发布等工作,并且也支持 PyPI 的 Trusted Publishers 机制,所以可以一步到位,省去了很多维护工作。如下是 Apopy 项目用于发布到 PYyPI 的 GitHub Actions 配置文件:

name: publish

on:
  push:
    tags:
      - "v*.*.*"

jobs:
  pypi-publish:
    name: upload release to PyPI
    runs-on: ubuntu-latest
    environment:
      name: PyPI
      url: https://pypi.org/p/apopy
    permissions:
      id-token: write
    env:
      TRUSTED_PUBLISHING: always
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version-file: "pyproject.toml"
      - name: Install uv
        uses: astral-sh/setup-uv@v3
        with:
          enable-cache: true

      - run: make build
      - run: uv publish

结语#

在公司项目和我个人的开源项目(如 tzfpyapopycyeva)中,都使用了这些方法,极大简化了维护工作。