本業はSRE, 相変わらず野球関係の趣味開発(個人開発)をしている者です.
今まで趣味開発におけるPythonのPackage Managerはpoetryを使っていましたが, 仕事で使っている&その他の理由でuvに変更しました.
このブログではそんなuvの話を書きたいと思います.
このブログについて
弊ブログ「Lean Baseball」では初の試みとして, 執筆の大半をCursorで行っています*1.
なお以下については中の人(shinyorke)の執筆となります.
- 挨拶など, 人間っぽい言い回しと脚注
- GitHub Actionsのコードスニペット
- サムネイルなどの画像
- 明らかな間違い, 見当違いっぽい内容
TL;DR
poetryからuvへの移行は学習コストが思ったより低かったです, パッケージのインストールとテストの実行が高速化された(気がします). 一方で, pyproject.tomlの書き換えやCIテストの移行にはやや苦労ししました.
移行の背景とメリット
Pythonのパッケージマネージャーとしてpoetryを使用していましたが, 以下の理由からuvへの移行を決断しました.
- 仕事のプロジェクトで使っているから(最大の理由*2)
- なんか流行っているっぽいから(2番目の理由)
移行は若干苦労しましたが, 開発効率的にもコードの見通し的にもスッキリした気がします.
学習コストの低さ
uvはpoetryと同様のコマンド体系を持っているため, 学習コストは非常に低かったです. 例えば, 以下のようなコマンドはほぼ同じように使用できます.
# poetryの場合 poetry add pandas poetry install poetry run pytest # uvの場合 uv add pandas uv install uv run pytest
ドキュメントを読んだ感じの雰囲気も察していましたが...想像以上に楽でした.
パフォーマンスの向上
uvはRustで実装されており, 以下の点で高速化が実現できました.
- 雰囲気的にtestやツール類の実行が速くなった気がする
- GitHub Actionsのtask実行は明確に高速化
こちらは導入前後のCI速度を見ると歴然でした.
移行で苦労した&変更した所
移行作業では以下で苦労したり変更したりしました. それぞれの課題に対して, どのように対処したかを説明します.
- pyproject.tomlの書き換え
- コードチェックをruffに変更
pyproject.tomlの書き換え
poetryの設定ファイルからuv用の設定ファイルへの移行には若干手間がかかりました.
- poetry用のpyproject.tomlを別名保存
- uvのpyproject.tomlをゼロから作成
- 元のpoetry用pyproject.tomlを見ながらライブラリを追加
今回はPythonで作ったAPI・workerバックエンドが3つありましたがすべてに対して愚直にこれを行ってテストしました.
今回はuvを覚えるために「あえて」AIに頼らず手動でやりましたが, 次からはCursorに頼って試そうと思っています.
コードチェックをruffに変更
一言で言うとruffに変更しました.
- type checkはmypy
- コードのチェックほかはruff
isort, blackはいいだろうと言うことで捨てました&mypyも無くて良かったかも(ruffでチェックしているっぽいので).
何をどう変更したかはGitHub Actionsの移行をご覧ください.
GitHub Actionsの移行
CI/CDパイプラインの移行も重要な作業でした. poetryからuvへの移行に伴い, GitHub Actionsのワークフローファイルも大きく変更しました.
主な変更点
移行前後のGitHub Actionsの設定を比較すると, 以下のような変更がありました.
パッケージマネージャーのセットアップ
- poetry:
abatilo/actions-poetry@v2.0.0
を使用 - uv:
astral-sh/setup-uv@v5
を使用
- poetry:
依存関係のインストール
- poetry:
poetry install
でシンプルにインストール - uv:
uv sync --all-extras --dev
で開発用の依存関係も含めて一括インストール
- poetry:
コード品質チェックツールの統合
- poetry: black, isort, mypyを個別に実行
- uv: ruffに統合し, format, checkを一括で実行
設定ファイルの比較
移行前(poetry)の設定:
dashboard-test: name: Dashboard Test runs-on: ubuntu-latest defaults: run: working-directory: "${{ env.ROOT_PATH_DASHBOARD }}" steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.12.5 - id: use-poetry name: Run image uses: abatilo/actions-poetry@v2.0.0 with: poetry-version: "1.8.3" - id: poetry-install name: Install & test run: poetry install - id: type-check name: type check run: poetry run mypy . - id: code-check name: code check run: poetry run black . - id: import-check name: import check run: poetry run isort . - id: run-test name: Run test run: poetry run pytest .
移行後(uv)の設定:
dashboard-test: name: Dashboard Test runs-on: ubuntu-latest defaults: run: working-directory: "${{ env.ROOT_PATH_DASHBOARD }}" steps: - uses: actions/checkout@v4 - id: install-uv name: Install uv uses: astral-sh/setup-uv@v5 - id: setup-python name: "Set up Python" uses: actions/setup-python@v5 with: python-version-file: "${{ env.ROOT_PATH_DASHBOARD }}/pyproject.toml" - id: install-project name: Install the project run: uv sync --all-extras --dev - id: format name: code format run: uvx ruff format . - id: type-check name: type check run: uvx mypy . - id: code-check name: code check run: uvx ruff check . --fix --output-format=github - id: run-test name: Run test run: uv run pytest .
主な差分:
steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: 3.12.5 - - id: use-poetry - name: Run image - uses: abatilo/actions-poetry@v2.0.0 - with: - poetry-version: "1.8.3" + - id: install-uv + name: Install uv + uses: astral-sh/setup-uv@v5 + - id: setup-python + name: "Set up Python" + uses: actions/setup-python@v5 + with: + python-version-file: "${{ env.ROOT_PATH_DASHBOARD }}/pyproject.toml" - - id: poetry-install - name: Install & test - run: poetry install + - id: install-project + name: Install the project + run: uv sync --all-extras --dev - - id: type-check - name: type check - run: poetry run mypy . - - id: code-check - name: code check - run: poetry run black . - - id: import-check - name: import check - run: poetry run isort . + - id: format + name: code format + run: uvx ruff format . + - id: type-check + name: type check + run: uvx mypy . + - id: code-check + name: code check + run: uvx ruff check . --fix --output-format=github - id: run-test name: Run test - run: poetry run pytest . + run: uv run pytest .
この変更により, CIの実行時間が結構短くなりました.
移行の手順
実際の移行手順は以下の通りです. 各ステップで注意すべき点を詳しく説明します.
既存のpoetry環境のバックアップ
pyproject.toml
をpyproject-poetry.toml
にリネーム- この時点で
poetry.lock
とpoetry.toml
は残しておく - バックアップを取ることで, 万が一移行に失敗した場合でも元の環境に戻せる
uvの初期化
uv init
コマンドで新しいpyproject.toml
を作成- この時点で
pyproject.toml
は最小限の設定のみが含まれる - プロジェクト名やバージョンなどの基本情報は自動的に設定される
依存関係の移行
pyproject-poetry.toml
を参考に, 必要なライブラリをuv add
コマンドで追加- 開発依存関係は
uv add --dev
で追加 - バージョン指定が必要な場合は
uv add package==version
の形式で指定 - 依存関係の解決に失敗した場合は, エラーメッセージを確認して必要な調整を行う
GitHub Actionsの更新
- poetryの設定をuvの設定に置き換え
- ruffの設定を追加し, コード品質チェックを統合
- テスト実行コマンドを
uv run
形式に変更
移行完了後のクリーンアップ
- 移行が完了し, すべてのテストが正常に動作することを確認
- 以下のファイルを削除
pyproject-poetry.toml
poetry.lock
poetry.toml
- 削除前に, これらのファイルの内容を確認し, 必要な設定が
pyproject.toml
に反映されていることを確認
結び
poetryからuvへの移行は, 多少の苦労はありましたが, 全体的に見れば成功だったと考えています.
開発体験的にも悪くないのと, 何よりも仕事で使っているモノに変更できたお陰で一石二鳥感もあったりします.
おそらく今後はpoetryは使わない(使う理由が無い)気がしています, ずっとuvなのかと言われるとそこも違う気がしますが...
以上, 「個人開発プロジェクトのPythonライブラリ管理をpoetryからuvに移行した話」でした.
最後までお読みいただきありがとうございました.
おまけ - Cursorはどこまでブログを書いたか
見ての通り, 結構な所をやってくれました.
緑の部分はshinyorkeが修正・加筆した所.