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 の基本情報を登録するだけで公開設定が完了します。設定プロセスは非常にシンプルで、メンテナンス工数を大幅に削減できます。

Trusted Publishers の追加手順 を確認すれば、すぐに導入可能です。

uv による超高速依存ロック

2024 年にリリースされた uv は、Python プロジェクトの依存ロック速度を桁違いに向上させました。従来の PDM や Poetry といったツールも便利ですが、CI 上での依存ロックはどうしても時間がかかってしまいがちです。私は GitHub 上に比較用のリポジトリを公開し、いくつかのツールで実際に依存ロックにかかる時間を計測しました(GitHub Actions 実行タイミングに基づく例):

ツール所要時間
Poetry43 秒
PDM1 分 7 秒
uv2 秒

uv を使えば秒単位で固定できるため、開発中の待ち時間はほとんど感じません。

また、機械学習モデルに依存するプロジェクトでは必然的に PyTorch エコシステムも必要になりますが、その場合でも以下のような課題があります:

  • CPU-only ビルドであってもプラットフォームごとに異なるタグを使い分ける必要がある。
  • サードパーティ製のライブラリが PyTorch バージョンに厳格な場合、誤ったバージョンを入れてしまうと Segmentation fault (core dumped) のような分かりにくいエラーで止まる。

これらを回避するために、私は uvindex 機能を活用して PyTorch 用のソースを明示的に指定しています。例えば、以下のような pyproject.toml であれば、macOS と Linux の両環境で同じバージョンの PyTorch を確実にインストールできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
[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 も次のように非常にシンプルに書けます。

1
RUN uv sync --compile --frozen

GitHub Actions での一体化公開

uv は依存固定だけでなく、ビルド・パッケージ・公開まで一気通貫でサポートし、PyPI の Trusted Publishers にも対応しています。たとえば私のオープンソースプロジェクト “apopy” では、以下のような GitHub Actions 設定でタグを push するだけで PyPI へ自動公開が完了します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

まとめ

社内プロジェクトから個人のオープンソース(tzfpy, apopy, cyeva など)に至るまで、これらの手法を導入することでメンテナンスコストを大幅に削減できました。