uvでPythonのバージョンを管理する — pyenvからの移行手順

公開日:
目次

複数のプロジェクトを行き来していると、3.10で動かすアプリと3.13で書いているスクリプトを同じMacで使いたい場面が出てきます。これまではpyenvで切り替えていましたが、uvに移ったら同じことが1コマンドで済むようになりました。やってみたら想像より単純で、しばらくpyenvに戻ろうとは思っていません。

uvが受け持つ範囲

uvはパッケージ管理だけでなく、Python本体のインストールと切り替えも担当します。pyenvでお馴染みのビルド済みバイナリ取得という役割を、uv自身が内側に持っている形です。

shim(呼び出しを横取りする仕組み)は差し込みません。uv run を叩いたときに、その作業ディレクトリに合うPythonを内部で選ぶ作りです。PATHには手を加えないので、別のシェルを開きっぱなしにしていても、そちらの動きは変わりません。

使えるバージョンを確認する

uv python list を打つと、システムに既にあるPythonと、ダウンロード可能なバージョンが並びます。

$ uv python list
cpython-3.13.4-macos-aarch64-none   /opt/homebrew/opt/[email protected]/bin/python3.13
cpython-3.13.2-macos-aarch64-none   <download available>
cpython-3.12.7-macos-aarch64-none   /Users/you/.local/share/uv/python/...
cpython-3.11.11-macos-aarch64-none  <download available>
...

右側にパスがある行はインストール済みです。<download available> はuvが必要に応じて取りに行きます。

pyenvで以前入れたバージョンもこのリストに自動的に含まれます。移行の初日に新しく入れ直す手間は基本ありません。

バージョンをインストールする

uv python install でPython本体を入れます。指定の書き方は柔軟です。

uv python install 3.12        # 最新の3.12系
uv python install 3.12.9      # パッチ番号まで指定
uv python install 3.10 3.11 3.12  # まとめて

保存先は ~/.local/share/uv/python/ 以下です。pyenvの ~/.pyenv/versions/ とは完全に独立した場所なので、両方を併用しても干渉しません。

プロジェクトのバージョンを固定する

pyenvからの乗り換えで一番気になる部分です。uv python pin でプロジェクトに .python-version を作成します。

$ uv python pin 3.12
Pinned `.python-version` to `3.12`

$ cat .python-version
3.12

ファイル形式はpyenv互換です。pyenvで作った既存の .python-version をそのままuvが読みます。手元のプロジェクトで試したときも、ファイルをいじる必要がなく uv run が正しいPythonを掴んでくれました。

uv runuv sync は、カレントディレクトリから親方向へ .python-version を探します。要件に合うPythonが手元になければ、その場で自動ダウンロードまで進みます。

全体のデフォルトを変える

pyenvでの pyenv global 3.12 に当たるのが --global フラグです。

uv python pin --global 3.12

設定はユーザー設定ディレクトリに保存され、.python-version の無いディレクトリで uv run を叩いたときの基準になります。

ハマったところ

乗り換え初日に引っかかったのは uv python list の見やすさです。Homebrewで入れたPython、uvが管理するPython、pyenvの残骸が一覧に並ぶので、何がどこから来たのか頭の中で整理する必要がありました。--only-installed を付けると <download available> の行が消えて、把握しやすくなります。

uv python list --only-installed

もう1つは pyproject.tomlrequires-python.python-version の優先順位です。requires-python = ">=3.10" のような幅のある指定があっても、.python-version の固定指定が同居するとそちらが勝ちます[1]。ローカルにファイルが残ったままCIへ持っていくと、CIのPythonまで巻き込まれて困ったことがありました。CI側で actions/setup-python などを使って固定し、.python-version はリポジトリに置かない方針も選択肢になります。

pyenvを残すか、捨てるか

uvに完全に寄せても、私の用途では困っていません。ただしPythonのビルドそのものに手を入れたい場合は、ソースから組み立てるpyenvがまだ便利です。OpenSSLを差し替えたい、ビルドフラグを変えたい、といった用途が該当します。uvが配布するのは事前ビルド済みのバイナリなので、ビルド時の介入はできません。

普段使いはuvで完結させて、特殊ビルドが必要なときだけpyenvを呼ぶ二重持ちも現実的です。完全にpyenvを消すのは、それで一度も困らない期間を数ヶ月挟んでからでも遅くないと思います。

公式の詳細はPython versionsのドキュメント[2]にまとまっています。3.13や3.14のfree-threaded版を試すような新しい話題も同じページから辿れます。

脚注
  1. Python versions - uv | Discovery of Python versions ↩︎

  2. Python versions - uv ↩︎