uvが減らすのは、速さより手順の分岐
Python projectでは、Python本体をpyenv、仮想環境をvenv、依存導入をpip、固定をpip-tools、CLI toolをpipx、配布をbuildやtwineで扱う構成がある。どれも役割は明確だが、新しいmemberは『このrepoではどれを、どの順で使うか』を覚えなければならない。
Astralのuvは、Python version管理、virtual environment、dependency resolution、lockfile、script実行、tool実行、package buildとpublishを一つのCLIへ寄せる。Rust製による速さが注目されるが、チームにとって大きいのは、uv syncとuv runを共通の入口にできることだ。既存のpip-compatible interfaceもあり、新規導入と段階移行の両方を狙っている。
Poetryやpipを、全部すぐ置き換える道具ではない
uvのproject workflowはpyproject.tomlとuv.lockを中心に動く。Poetryと重なる部分は多いが、既存projectのbuild backendや公開方法まで自動的に同じ意味へ変換するわけではない。pip-compatible commandは、requirements中心の既存運用を維持したままresolverやinstallを置き換える場合に使える。
採用判断では機能数より、どのsource of truthを選ぶかを決める。新規applicationならpyprojectとuv.lockへ揃えやすい。既にPoetry lock、private index、editable package、platform markerを運用しているなら、同じdependency graphとbuild artifactが再現できるかを先に試す。
| 構成 | 主なsource of truth | 向く状況 | 移行時の注意 |
|---|---|---|---|
| uv project | pyproject.toml + uv.lock | 新規開発、複数platform、統一CLI | build backendとindex設定を確認 |
| pip + requirements | requirements*.txt | 単純な既存運用、広い互換性 | Python本体やlock生成は別管理 |
| Poetry | pyproject.toml + poetry.lock | 既存Poetry workflow | lockの意味と公開設定を一括変換しない |
| pipx / uvx | toolごとの隔離環境 | CLIをproject依存へ入れず試す | 一時実行と永続installを分ける |
隔離環境で、initからfrozen syncまで確認した
2026年9月17日、macOS x86_64の一時directoryへ公式installerでuv 0.12.15を配置し、repositoryやglobal Python設定を変更せず検証した。systemのCPython 3.13.2を使ってapplication projectを作り、httpx 0.28.1を追加した。uvは8 packageをresolveし、.venvとuv.lockを作成した。
続けてuv runでhttpxをimportし、uv lock --checkとuv sync --frozenが成功することを確認した。これは性能benchmarkではない。download状況やcacheで時間は変わる。ここで確認したかったのは、公式の基本commandが一つの隔離projectで、初期化、依存追加、実行、lock検証、再現までつながることだ。
| 確認項目 | 環境・結果 | 記事での扱い |
|---|---|---|
| uv | 0.12.15 | このversionでcommandを実行 |
| Python | CPython 3.13.2 | 既存system interpreterを利用 |
| dependency | httpx 0.28.1を含む8 package | uv.lockへ解決結果を記録 |
| 再現確認 | lock --check / sync --frozen 成功 | CI向け手順の動作を確認 |
新規projectは、四つのcommandで流れを覚える
uv initはproject metadataとsample sourceを作り、uv addはpyprojectへdependencyを追加してlockと環境を更新する。uv runはproject environmentでcommandを実行し、必要なら事前に同期する。uv syncはlockに沿ってenvironmentを明示的に揃える。activateは可能だが、日常commandをuv runへ寄せればshell状態への依存を減らせる。
Python versionはuv python pinで.python-versionへ残せる。ただし、applicationが本当に3.13以上を要求するのか、開発者の好みで固定しただけかを分ける。libraryならsupport rangeをrequires-pythonへ書き、CIで複数versionを検証するほうが適切な場合がある。
uv init --app hello-uv --python 3.13
cd hello-uv
uv add httpx
uv add --dev pytest ruff
uv run python -c 'import httpx; print(httpx.__version__)'
uv run ruff check .
uv run pytestuv.lockとpyproject.tomlの役割を分ける
pyproject.tomlは、人が管理するdirect dependencyとversion条件を表す。uv.lockは、transitive dependency、artifact、platform条件を含む解決結果を再現するためのmachine-managed fileだ。applicationでは通常lockfileをcommitする。libraryでも開発・test環境の再現には有用だが、利用者へ同じtransitive versionを強制するものではない。
uv sync --frozenはlockを更新せず同期し、pyprojectとの不整合があれば失敗する。CIで意図しないlock更新を防ぐのに向く。uv lock --checkはlockが最新かを確認する。dependency更新はapplication変更と別diffにし、対象を絞るならuv lock --upgrade-package httpxのように指定する。
uv lock --check
uv sync --frozen
uv run pytest
uv run ruff check .
# 対象を絞ったdependency更新
uv lock --upgrade-package httpx
uv run pytestCIでは、uv自体とdependency cacheを分けて考える
CIは、uvを再現可能な方法で導入し、Pythonを揃え、uv sync --frozenを行い、uv runで検証する。公式GitHub Actionを使う場合もversionを固定し、Action自体の参照方法は組織のsupply-chain policyに合わせる。cacheは高速化であり、再現性の根拠ではない。cacheを消してもlockから構築できる状態を保つ。
private indexではcredentialをpyprojectやlockへ書かない。CI secretから必要なprocessへだけ渡し、logへURLやtokenを出さない。native extensionがあるprojectは、Python packageだけでなくOS library、compiler、architectureも再現条件へ含める。uvがinstallに成功してもapplicationが動くことは保証しないため、testとbuildまでを完了条件にする。
uv python install 3.13
uv lock --check
uv sync --frozen --all-groups
uv run ruff check .
uv run pytest
uv buildrequirementsからは、変換と採用を分ける
公式guideにはuv add -r requirements.txtでdependencyをprojectへ取り込む方法がある。だが、変換commandが成功しても、environment marker、private index、editable install、constraints、hash policyが同じ意味で表現されたとは限らない。生成されたpyprojectとlockを読み、旧環境とtest結果を比較する。
安全な移行は、作業branchでuvを追加し、既存testをuv run経由で通し、次にCIを切り替え、最後に旧scriptを削除する。Dockerfile、release、publishまで同じPRへ入れると、失敗原因が増える。まずdevelopmentとCIの再現から始める。
uv init --bare
uv add -r requirements.txt
uv add --dev -r requirements-dev.txt
uv lock
uv run pytest
git diff -- pyproject.toml uv.lock採用条件は、clean checkoutで決める
uvを入れたことではなく、clean checkoutから指定Python、dependency、test、buildを再現できたことを採用条件にする。既存memberのcacheが効いたmachineだけで成功しても不十分だ。macOSとLinux、必要ならWindowsでlockを検証し、native dependencyを含むartifactも確認する。
uvはPython ecosystemの複雑さを隠してくれるが、消してはくれない。build backend、wheel、index、platform markerを理解したうえで、日常操作を狭いinterfaceへまとめるtoolだ。小さなsampleで再現性を確認できたら、一つのserviceから段階的に広げるのがよい。
- pyprojectを人の宣言、uv.lockを解決結果としてreviewする
- CIではlock更新を許さず、dependency更新を別taskにする
- private indexとnative dependencyを移行前に洗い出す
- clean checkoutでtestとbuildが通るまで旧workflowを残す