uv python は、Rust製のPythonパッケージマネージャ uv が持つ「Python本体のバージョン管理」機能です。pyenv でやっていたことを uv 1つに寄せられます。本記事では install / list / find / pin / upgrade の役割分担を実測で整理し、あわせて uv 0.12 で uv python の挙動が変わった2点を、0.11.9 と 0.12.6 を並べて動かしたA/Bで確かめます。フラグの一覧は両バージョンで完全に同一なので、コマンドを見ても気づけない種類の変化です。
30秒でわかる uv python
・uv python install はPython本体を入れるコマンド。uv venv(仮想環境)や uv run(実行)とは役割が別
・プリビルト取得なのでビルドしない。実測で 3.12.13 が 23.8MiB ダウンロード+1秒未満で導入完了
・【0.12の落とし穴①】--reinstall が最新へ上げなくなった。実測: 0.11.9 は 3.12.13 を新規取得、0.12.6 は既存の 3.12.8/3.12.9 を再インストールするだけ。上げたいなら --upgrade
・【0.12の落とし穴②】bzip2配布の古いPyPyが消えた。実測: uv python list 3.10 --all-versions の PyPy が 4件→1件
・フラグ一覧は 0.11.9 と 0.12.6 で完全一致。--help の差分はゼロで、変わったのは挙動だけ
開発まわりの自動化ツール全体の見取り図はAI自動化ツール|ノーコードからコードまで2026年版の比較と選び方にまとめてあります。本記事はそのうちPython環境の足回りの話です。
uv python とは — Pythonのバージョン管理をuvに寄せる
uv(astral-sh/uv・★89,140・Apache-2.0)は Rust 製のPythonパッケージマネージャですが、パッケージだけでなくPythonインタプリタそのものも管理します。それが uv python サブコマンド群です。
実測時点(2026-08-27)で uv python --help が並べるサブコマンドは8つです。
| サブコマンド | 役割 |
|---|---|
install |
Pythonをダウンロードして導入する |
list |
利用可能/導入済みのPythonを一覧する |
find |
条件に合うPythonのパスを探す |
pin |
プロジェクトのバージョンを .python-version に固定する |
upgrade |
導入済みPythonをアップグレードする |
uninstall |
導入済みPythonを削除する |
dir |
uvがPythonを置くディレクトリを表示する |
update-shell |
Python実行ファイルのディレクトリを PATH に通す |
pyenv との最大の違いはビルドしないことです。uv は python-build-standalone のプリビルトバイナリを取得します。実測では 3.12.13 の取得が 23.8MiB のダウンロードで、インストール自体は 985ms で完了しました。pyenv がソースビルドに数分かけるのと比べると体感が違います。
一方で、--enable-optimizations のようなビルドオプションを自分で指定したい用途は uv の守備範囲外です。そこは pyenv や自前ビルドが引き続き必要になります。
uv と同じく「Rust で書き直して速くする」系の Python ツールとしては Polars入門:pandasの10倍速でデータ処理できるRust製DataFrameライブラリ があり、データ処理側で同じ流れが起きています。
uv python install — Pythonを入れる
もっとも基本のコマンドです。バージョンは複数まとめて渡せます。
# 単一バージョン
uv python install 3.12
# 複数まとめて
uv python install 3.12.8 3.12.9
実測では2バージョンの導入が 965ms で終わりました。
Installed 2 versions in 965ms
+ cpython-3.12.8-macos-aarch64-none
+ cpython-3.12.9-macos-aarch64-none (python3.12)
導入先を変えたい場合は --install-dir、環境変数なら UV_PYTHON_INSTALL_DIR を使います。CIやコンテナで置き場所を固定したいときに便利です。
UV_PYTHON_INSTALL_DIR=/opt/pythons uv python install 3.12
導入されたPythonは uv の管理ディレクトリに置かれ、システムのPythonには触れません。uv python dir でその場所を確認できます。システムのPythonを壊さないのは、pyenv の shim 方式と比べたときの安心材料です。
uv python install を明示的に叩く場面は意外と少ない
日常的には uv run が自動で面倒を見ます。プロジェクトの .python-version や pyproject.toml の requires-python を見て適切なインタプリタを選び、無ければダウンロードまでします。
uv python install を明示的に使うのは、次のような場面です。
・CIで先にバージョンを確定させたい(ビルドステップとテストステップを分けたい)
・オフライン/制限環境の事前準備として、必要なPythonを先に落としておきたい
・複数パッチを同時に持ちたい(互換性検証など)
uv run に任せる場合の流れを図にすると、uv python の各サブコマンドがどこで効くかが分かります。
または requires-python は?"} B -->|"あり"| C{"その版は
導入済みか?"} B -->|"なし"| D["システム/既定のPythonを選ぶ"] C -->|"はい"| E["そのインタプリタを使う"] C -->|"いいえ"| F["自動ダウンロードして使う"] G["uv python pin"] -.->|".python-version を書く"| B H["uv python install"] -.->|"事前に用意しておく"| C I["uv python upgrade"] -.->|"最新パッチへ上げる"| C
点線が「人が明示的に打つコマンド」です。実線の流れは uv run が勝手にやってくれる部分で、だからこそ uv python install を直接叩く機会は限られます。
uv python list / find / dir — いま何が入っているかを見る
list は「入っているもの」と「入れられるもの」を両方出します。導入済みだけに絞るなら --only-installed です。
# 3.12系で入手可能なパッチを全部見る
uv python list 3.12 --all-versions
# 導入済みだけ
uv python list --only-installed
# uvが管理するPythonの置き場所
uv python dir
list の出力は、システムのPython(Homebrew等)と uv 管理のものが混在して表示される点に注意してください。実測でも Homebrew の python3.12 と uv が入れた 3.12.9 が並んで出ました。どちらを指しているかはパスで見分けます。
cpython-3.12.9-macos-aarch64-none /opt/homebrew/bin/python3.12 -> ...
cpython-3.12.9-macos-aarch64-none /tmp/uv12/pythons/cpython-3.12-.../bin/python3.12
find は「条件に合うインタプリタの実体パス」を1つ返します。スクリプトから使いやすい形です。
uv python find 3.12
# => /path/to/pythons/cpython-3.12-macos-aarch64-none/bin/python3.12
uv python pin — プロジェクトごとにバージョンを固定する
pin は .python-version を書くだけの単純なコマンドですが、これが uv run の挙動を決めます。
uv python pin 3.12
# => Pinned `.python-version` to `3.12`
実測すると .python-version の中身はそのまま 3.12 の1行でした。パッチまで固定したいなら uv python pin 3.12.9 のように書きます。
チームで揃えたいなら .python-version をコミットします。 pyenv と同じファイル名・同じ役割なので、pyenv から移行する場合はそのまま流用できます。
pyproject.toml の requires-python との使い分けも押さえておくと迷いません。requires-python は「このパッケージが動作を保証する範囲」というライブラリとしての宣言で、>=3.10 のような幅を書きます。.python-version は「この作業ツリーで実際に使う版」という開発者側の選択で、1点を書きます。両方あるとき uv run は .python-version を優先しつつ、requires-python の範囲を満たすかを検査します。範囲外を pin していると実行時に弾かれるので、ライブラリ開発では両者が矛盾していないか確認してください。
【0.12の落とし穴①】--reinstall はもう最新へ上げない
ここからが本題です。uv 0.12.0(2026-07-28リリース)は、uv python install --reinstall の意味を変えました。公式リリースノートの記述はこうです。
Reinstall matching installed Python patch versions instead of upgrading implicitly Before Python upgrades were supported,
uv python install 3.12 --reinstalldoubled as a way to install the latest Python 3.12 patch release. Now that--upgradeis available,--reinstallreinstalls the matching patch releases that are already present.
これを同じ状態から両バージョンで実際に走らせて確かめました。前提として 3.12.8 と 3.12.9 をインストール済みにし、UV_PYTHON_INSTALL_DIR で隔離した同一のディレクトリを使っています。
| uv 0.11.9 | uv 0.12.6 | |
|---|---|---|
| コマンド | uv python install 3.12 --reinstall |
同じ |
| ダウンロード | 新規に 3.12.13 を取得(23.8MiB) | なし |
| 結果 | + cpython-3.12.13 が追加される |
~ cpython-3.12.8 / ~ cpython-3.12.9 を再インストール |
| 意図した動作か | 「最新に上げる」つもりなら○ | 「最新に上げる」つもりなら× |
0.11.9 の実出力:
Downloading cpython-3.12.13-macos-aarch64-none (download) (23.8MiB)
Downloaded cpython-3.12.13-macos-aarch64-none (download)
Installed Python 3.12.13 in 985ms
+ cpython-3.12.13-macos-aarch64-none
0.12.6 の実出力:
~ cpython-3.12.8-macos-aarch64-none
~ cpython-3.12.9-macos-aarch64-none
+(追加)と ~(再インストール)で記号まで変わっています。 0.12.6 はネットワークアクセスすら発生していません。
正しい上げ方は --upgrade
同じ 0.12.6 で --upgrade を実行すると、期待どおり最新が入りました。
uv python install 3.12 --upgrade
# Downloaded cpython-3.12.14-macos-aarch64-none (download)
# Installed Python 3.12.14 in 907ms
# + cpython-3.12.14-macos-aarch64-none
専用のサブコマンド uv python upgrade 3.12 も同じ役割です。最新パッチだけを入れ直したい場合は --upgrade --reinstall を組み合わせます。
--help のフラグ一覧を 0.11.9 と 0.12.6 で機械的に差分を取ったところ、差分はゼロでした。-r, --reinstall も -U, --upgrade も両方に存在し、説明文も同じ位置にあります。変わったのは挙動だけで、コマンドの見た目からは判別できません。CIスクリプトに --reinstall を「最新取得」の意味で書いている場合、uvを上げた日から黙って古いパッチを使い続けることになります。なお実測で 0.11.9 が取得したのは 3.12.13 で、0.12.6 の --upgrade が取得したのは 3.12.14 でした。「最新」はuv本体のバージョンが知っているマニフェスト次第という点も、あわせて覚えておくと混乱が減ります。
【0.12の落とし穴②】古いPyPyが uv python install から消えた
もう1件は配布形式に起因する変更です。0.12 は bzip2 圧縮のアーカイブのサポートを落としました。その結果、bzip2 でしか配布されていない古い PyPy のパッチリリースが取得できなくなりました。
これも実測で確認できます。同じコマンドの出力を並べます。
uv python list 3.10 --all-versions | grep pypy
| uv バージョン | 表示された PyPy 3.10 |
|---|---|
| 0.11.9 | pypy-3.10.16 / pypy-3.10.14 / pypy-3.10.13 / pypy-3.10.12(4件) |
| 0.12.6 | pypy-3.10.16(1件) |
各マイナーバージョンの最新 PyPy は gzip 配布なので残っています。消えたのは古いパッチだけです。リリースノートも「オプトアウトはできない。新しいPyPyのパッチリリースを指定してほしい」と明記しています。
PyPy を特定パッチに固定していたプロジェクトは、uv を 0.12 に上げた時点で解決に失敗する可能性があります。CIでPyPyのパッチを固定している場合は、上げる前に uv python list <version> --all-versions で在庫を確認してください。
uv 0.12 のその他の変更で uv python 利用者に効くもの
0.12.0 のリリースノートは22件の破壊的変更を挙げていますが、公式は「ほとんどのユーザーは変更なしでアップグレードできる」としています。uv python の文脈で押さえておく価値があるものを挙げます。
インタプリタを置き換えうる wheel を拒否するようになった。 python という名前のエントリポイントは以前から拒否されていましたが、Python のような大文字違いは通っていました。macOS や Windows の大文字小文字を区別しないファイルシステムでは、これが仮想環境のインタプリタを上書きしうる——という指摘です。.data/scripts 配下や .data/data/bin/python のようなパス経由の置き換えも塞がれました。オプトアウト不可です。
uv init が既定でビルドシステムを定義するようになった。 uv init example は src/example レイアウトと uv_build の [build-system]、[project.scripts] を作ります。従来のレイアウトが欲しい場合は uv init --no-package です。既存プロジェクトには影響しません。
壊れた .venv シンボリックリンクを環境検出時に拒否する。 リンク先が消えた .venv を黙って使おうとしなくなりました。
--project の不正なパスを拒否する。 以前は存在しないパスを渡しても通っていました。タイポに気づけるようになった一方、--project を動的に組み立てているスクリプトは失敗するようになります。
uv run に渡したスクリプトを基準にプロジェクトを探すようになった。 以前はカレントディレクトリ基準でした。別ディレクトリのスクリプトを uv run path/to/other/script.py のように実行している場合、参照されるプロジェクトが変わる可能性があります。
プレリリースの選び方が変わった。 既定が if-necessary-or-explicit から if-necessary になり、まず安定版を試してから必要な場合だけプレリリースへ落ちる形になりました。依存の依存が example>=2.0.0b1 のようにプレリリースを要求するケースが解決できるようになった一方、安定版とプレリリースの両方が候補にあるとき、以前と違うバージョンが選ばれることがあります。固定したいなら --prerelease disallow です。
これらはいずれも uv python そのものの変更ではありませんが、Pythonのバージョンを固定して再現性を確保している環境ほど影響を受けやすい項目です。ロックファイルを持つプロジェクトなら、0.12 に上げた直後に uv lock --check 相当の確認を1回挟むと、解決結果が変わっていないかを早く検知できます。
移行時に確認すべきこと
uv を 0.11 系から 0.12 系へ上げるときの確認項目を、uv python に絞ってまとめます。
① --reinstall を「最新取得」の意味で使っていないか。 CIスクリプトや Makefile、Dockerfile を grep してください。使っていれば --upgrade に書き換えます。
# リポジトリ全体を確認する
grep -rn "python install.*--reinstall" . --include=Makefile --include=Dockerfile --include="*.yml" --include="*.sh"
② PyPy のパッチを固定していないか。 .python-version や CI 設定に pypy-3.10.13 のような具体的パッチが書かれていたら、そのバージョンが 0.12 でも取得できるか uv python list で確認します。
③ uv_build の上限。 [build-system] に uv_build<0.12 のような上限を書いているなら uv_build>=0.11.32,<0.13 に広げます。
ローカル開発環境まわりの取り回しを整える話としては portlessとは|localhostのポート番号を名前付きURLに置き換えるVercel Labs製CLIを実測 も同じ系統です。どちらも「毎回ちょっと面倒なこと」を土台側で消すツールです。
④ 手元で1回A/Bを取る。 本記事の実測は、公式tarballを展開して既存のuvと並置し、UV_PYTHON_INSTALL_DIR と UV_CACHE_DIR で状態を隔離しただけです。既存環境を壊さずに挙動差を確認できます。
# 0.12系を隔離して取得(既存のuvには触らない)
curl -sL https://github.com/astral-sh/uv/releases/download/0.12.6/uv-aarch64-apple-darwin.tar.gz | tar xz
export UV_PYTHON_INSTALL_DIR=/tmp/uvtest/pythons UV_CACHE_DIR=/tmp/uvtest/cache
# フラグ一覧の差分を取る(実測では差分ゼロだった)
diff <(uv python install --help | grep -oE '\-\-[a-z-]+' | sort -u) <(./uv-aarch64-apple-darwin/uv python install --help | grep -oE '\-\-[a-z-]+' | sort -u)
⑤ バージョンを固定して段階的に上げる。 uv は 0.12.0 が 2026-07-28、0.12.6 が 2026-08-25 と更新が速いプロジェクトです。CIでは uv 自体のバージョンも固定しておくと、挙動が変わった日を特定しやすくなります。
まとめ
・uv python は Python 本体のバージョン管理を uv に寄せる仕組み。install / list / find / pin / upgrade など8サブコマンド
・ビルドせずプリビルトを取得するため速い。実測で 3.12.13 が 23.8MiB・985ms
・【0.12の落とし穴①】--reinstall が最新へ上げなくなった。実測A/B: 0.11.9 は 3.12.13 を新規取得、0.12.6 は既存の 3.12.8/3.12.9 を再インストールするだけ。上げるなら --upgrade(0.12.6 で 3.12.14 が入ることを確認)
・【0.12の落とし穴②】bzip2配布の古いPyPyが消えた。実測で PyPy 3.10 の一覧が 4件→1件
・--help のフラグ差分はゼロ。挙動だけが変わったので、コマンドを見比べても気づけない
・移行時は --reinstall の用法・PyPyのパッチ固定・uv_build の上限の3点を確認する
uv python list --all-versions と --help の差分を1回取るだけで、自分の環境が0.12でどう変わるかはその場で確認できます。
参照ソース
- astral-sh/uv — 公式リポジトリ。★89,140・Apache-2.0・最終push 2026-08-27(2026-08-27 実測)
- uv 0.12.0 リリースノート — 破壊的変更22件の一次ソース。
--reinstallの挙動変更・PyPy の bzip2 配布廃止・インタプリタ置換wheelの拒否(2026-08-27 参照) - uv 公式ドキュメント — Installing Python —
uv pythonの公式ガイド(2026-08-27 参照) - uv 0.11.9(Homebrew)と 0.12.6(公式tarball)を同一マシンに並置し、
UV_PYTHON_INSTALL_DIR/UV_CACHE_DIRで状態を隔離して実行したA/B(macOS aarch64・2026-08-27 実測)