2024年9月: さらなる進化を遂げた「uv」の新機能¶
福田(@JunyaFff)です。本連載 Python Monthly Topicsで2024年3月に公開したRust製のPythonパッケージ管理ツール「uv」を使ってみようで紹介した「uv」が、さらなる進化を遂げました。今回は、その新機能を紹介します。
はじめに¶
Astral社が開発するRust製の高速なpipの代替ツール「uv」がパッケージマネージャーとして8月にアップデートされました。
pipの代替ツールとしてだけでなく、 Pythonプロジェクト、 コマンドラインツール、 単一ファイルスクリプトさらにPython自体を管理できるようになりました。
uv
は、 pip
や pipx
、 venv
、 poetry
や pyenv
のような機能を包括していると言え、そしてそのすべてが非常に高速に動作します。
本記事では、アップデートした「uv」の新機能を中心に紹介します。 基本的な使い方はRust製のPythonパッケージ管理ツール「uv」を使ってみよう を合わせてお読みください。
uv
の公式ドキュメント、GitHubは以下になります。
インストールと自動補完の設定¶
前回の記事でも紹介していますが、まずは、インストールと自動補完の設定を行いましょう。Linux、macOS、WIndowsに対応しています。
# Linux or macOS
$ curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
$ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
上記でインストールした場合には、 self update
コマンドにより最新版への更新ができます。
$ uv self update
また、HomebrewやCargo、pipx、Wingetでもインストールができます。
# Homebrew
$ brew install uv
# Cargo
$ cargo install --git https://github.com/astral-sh/uv uv
# pipx
$ pipx install uv
# Winget
$ winget install --id=astral-sh.uv -e
次にコマンドの入力補完を設定します。zshの場合は以下を実行してください。
$ echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc
$ source ~/.zshrc
そのほかのシェルについては、shell-autocompletionを参照してください。
なお、本記事で紹介するバージョンは 0.4.13
です。
$ uv --version
uv 0.4.13 (Homebrew 2024-09-19)
機能紹介¶
はじめに uv
コマンドを実行してみましょう。ヘルプが表示され、多くの機能が備わっていることが分かります。前回の記事では venv
と pip
だけが主な機能でしたが、今回はさらに多くのコマンドが追加されています。
本記事ではこの中から、いくつかの機能を紹介します。
$ uv
An extremely fast Python package manager.
Usage: uv [OPTIONS] <COMMAND>
Commands:
run Run a command or script
init Create a new project
add Add dependencies to the project
remove Remove dependencies from the project
sync Update the project's environment
lock Update the project's lockfile
export Export the project's lockfile to an alternate format
tree Display the project's dependency tree
tool Run and install commands provided by Python packages
python Manage Python versions and installations
pip Manage Python packages with a pip-compatible interface
venv Create a virtual environment
build Build Python packages into source distributions and wheels
cache Manage uv's cache
version Display uv's version
help Display documentation for a command
...
それぞれのサブコマンドについてもヘルプが表示されます。
$ uv run
Run a command or script
Usage: uv run [OPTIONS] <COMMAND>
...
また、入力補完を設定していると、コマンドの入力途中でタブキーを押下することでサブコマンドやオプションが参照できます。
$ uv python
dir -- Show the uv Python installation directory
find -- Search for a Python installation
install -- Download and install Python versions
list -- List the available Python installations
pin -- Pin to a specific Python version
uninstall -- Uninstall Python versions
公式ドキュメント: コマンドリファレンス Commands | uv
プロジェクト管理と依存関係の追加・管理¶
プロジェクト管理機能について紹介します。 uv
を利用し、 pyproject.toml
で外部ライブラリの依存関係やPythonのバージョンなど、プロジェクトの情報を一元管理できます。また uv.lock
というロックファイルにより依存関係を厳密に管理し、クロスプラットフォームでの再現性を向上させます。
公式ドキュメント:
以下のコマンドを利用し、プロジェクト管理の概要を説明します。
uv init
: プロジェクトの初期化uv run
: Pythonファイルの実行依存関係
uv add
: 追加uv remove
: 削除uv sync
: 同期
まず、 uv init
コマンドでプロジェクト作成します。 pyproject.toml
には、プロジェクトの最小限の情報が記述されます。
$ uv init hello-world
# Pythonバージョンを指定する場合
$ uv init hello-world --python 3.12
ディレクトリ構造と pyproject.toml
の内容は以下のようになります。
$ ls hello-world
README.md hello.py pyproject.toml
$ cd hello-world
$ cat pyproject.toml
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
uv init
コマンドには、プロジェクトの目的ごとに指定するオプションがあります。必要に応じて指定してください。
uv init --app project_name
: アプリケーション全般(デフォルト)uv init --lib project_name
: 配布するライブラリuv init --package project_name
: 配布するコマンドラインツール
続いて、作成されたPythonファイルを実行してみましょう。 uv run
コマンドでプロジェクトのPythonバージョンで実行できます。また仮想環境( .venv
)も自動で作成されます。 uv run
コマンドの詳細は本記事の後半で紹介します。
$ uv run hello.py
Using Python 3.12.6
Creating virtual environment at: .venv
Hello from hello-world!
依存関係の追加には、 uv add
コマンドを使用します。 uv add
は、 pyproject.toml
に依存関係を追加し、仮想環境にインストールも行います。仮想環境がない場合には自動で作成されます。
ここでは、 httpx
ライブラリを追加してみます。
$ uv add httpx
Resolved 8 packages in 3ms
Installed 7 packages in 6ms
+ anyio==4.6.0
+ certifi==2024.8.30
+ h11==0.14.0
+ httpcore==1.0.5
+ httpx==0.27.2
+ idna==3.10
+ sniffio==1.3.1
# pyproject.toml に依存関係が追加されていることを確認
$ cat pyproject.toml
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.27.2",
]
除外する場合には、 uv remove
コマンドを使用します。 uv remove
は、 pyproject.toml
から依存関係を削除し、仮想環境からも削除します。
$ uv remove httpx
uv remove httpx
Resolved 1 package in 2ms
Uninstalled 7 packages in 15ms
- anyio==4.6.0
- certifi==2024.8.30
- h11==0.14.0
- httpcore==1.0.5
- httpx==0.27.2
- idna==3.10
- sniffio==1.3.1
# pyproject.toml から依存関係が削除されていることを確認
$ cat pyproject.toml
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
手動で pyproject.toml
を更新した場合や、クローンしてきたリポジトリが uv
で管理されている場合には、 uv sync
コマンドを実行し環境を同期しましょう。uv.lockファイルと仮想環境を更新します。
$ uv sync
Using Python 3.12.6
Creating virtual environment at: .venv
Resolved 1 package in 10ms
Audited in 0.06ms
なお、 uv
で管理しているプロジェクトの場合、 uv pip install
ではなく、 uv add
や uv sync
を使うことが公式ドキュメントで推奨されています。 Projects | uv
新しく作成したプロジェクトの基本的な利用方法を紹介しました。プロジェクト全体を uv
で管理することで、依存関係の管理やPythonのバージョン管理が容易になります。プロジェクト全体の管理が難しい場合には、仮想環境や pip
の代替として、あるいは後述するコマンドラインツールやスクリプトの実行、Pythonバージョンの管理だけを行うのにフォーカスしても良いでしょう。
また、既存のプロジェクトを uv
で管理することもできます。例として公式ドキュメントに FastAPI のプロジェクトを uv
で管理する方法が紹介されています。
既存プロジェクトの移行例: FastAPI | uv
uv でのPythonの管理¶
uv python install
コマンドでPythonをインストールできます。マイナーバージョンの指定も可能です。
uv
では python-build-standalone
プロジェクトで公開されているPythonをインストールします。[1]
公式ドキュメント:
$ uv python install 3.13
Searching for Python versions matching: Python 3.13
Installed Python 3.13.0rc2 in 1.91s
+ cpython-3.13.0rc2-macos-aarch64-none
# マイナーバージョン指定も可能
$ uv python install 3.12.6
Searching for Python versions matching: Python 3.12.6
Installed Python 3.12.6 in 1.50s
+ cpython-3.12.6-macos-aarch64-none
# 複数バージョンのインストールも可能
$ uv python install 3.9 3.10 3.11
ただ、現時点では、uvでPythonをインストールしても、ターミナルで python
コマンドを実行しグローバルに利用することはできません。この機能のサポートは将来のリリースで予定されてるようです。
uv run
を使用するか、 uv venv
で仮想環境を作成して利用しましょう。
$ uv venv --python 3.12.6
なお、すでにインストール済みのPythonバージョンを指定した場合、 uv
はそのバージョンを再インストールしません。
uv python list
コマンドで、インストール済みのPythonのバージョンとそのPathの一覧を確認できます。
$ uv python list
cpython-3.13.0rc2-macos-aarch64-none /Users/jrfk/.local/share/uv/python/cpython-3.13.0rc2-macos-aarch64-none/bin/python3 -> python3.13
cpython-3.13.0b1-macos-aarch64-none /Library/Frameworks/Python.framework/Versions/3.13/bin/python3 -> python3.13
cpython-3.12.6-macos-aarch64-none /Users/jrfk/.local/share/uv/python/cpython-3.12.6-macos-aarch64-none/bin/python3 -> python3.12
cpython-3.12.4-macos-aarch64-none /opt/homebrew/opt/python@3.12/bin/python3.12 -> ../Frameworks/Python.framework/Versions/3.12/bin/python3.12
...
$ uv venv --python 3.12.4
Using Python 3.12.4 interpreter at: /opt/homebrew/opt/python@3.12/bin/python3.12
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
uv tool run によるコマンドラインツールの実行¶
ruff
や mypy
などのPythonコマンドラインツールを明示的にインストールすることなく利用できる機能です。
プロジェクトの仮想環境とは別に、一時的な仮想環境にインストールされるため、プロジェクト内のパッケージとは依存関係なく利用できます。
公式ドキュメント:
概要: Using tools | uv
詳細: Tools | uv
$ uv tool run mypy
# uv tool run のエイリアスは uvx
$ uvx mypy
もちろん、明示的にインストールしシステム全体で利用も可能です。
$ uv tool install mypy
$ mypy
uv run でのスクリプトの実行¶
uv run
コマンドでPythonのスクリプトを実行できます。Pythonのバージョンを指定した実行が可能です。
また、依存関係のあるスクリプトも容易に実行できます。
公式ドキュメント:
print("Hello world")
$ uv run hello.py
Hello world
Pythonのバージョンを指定して実行するには以下のようにします。
import sys
print(".".join(map(str, sys.version_info[:3])))
$ uv run --python 3.10 version.py
3.10.10
$ uv run --python 3.12 version.py
3.12.6
次に依存関係のあるスクリプトを実行してみましょう。以下のスクリプトでは、rich
ライブラリを使用しています。[2]
import time
from rich.progress import track
for i in track(range(20), description="For example:"):
time.sleep(0.05)
そのまま実行しようとするとエラーになります。
$ uv run dep.py
Traceback (most recent call last):
File "dep.py", line 2, in <module>
from rich.progress import track
ModuleNotFoundError: No module named 'rich'
--with
オプションで依存関係のあるライブラリ(この場合 rich
)を指定することで、依存関係を解決して実行できます。
$ uv run --with rich dep.py
For example: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01
さらに、スクリプト内に依存関係を記述することで、依存関係を自動で解決できます。依存関係の追加は、 uv add --script
コマンドで行います。
インラインでの定義は、PEP 723 – Inline script metadata | peps.python.org によって提案されています。
$ uv add --script dep.py "rich"
Updated `dep.py`
$ cat dep.py
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "rich",
# ]
# ///
import time
from rich.progress import track
for i in track(range(20), description="For example:"):
time.sleep(0.05)
スクリプト内に依存関係を追加することで、依存関係を解決し実行できます。スクリプトを配布する際に、 requirements.txt
を用意して、、といった手間が省けます。
$ uv run dep.py
Reading inline script metadata from: dep.py
For example: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:01
また、スクリプトの再現性の向上のため、タイムスタンプの指定を exclude-newer
オプションで行うことができます。
いくつかの複雑な依存関係を持つスクリプトにして、実行してみましょう。Pythonバージョンを指定することで、より再現性を向上させます。
asks
, anyio
ライブラリを使用して、非同期でポケモンの名前を取得するスクリプトです。コメントにPythonバージョンと依存関係、exclude-newerを指定しています。
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "asks",
# "anyio",
# ]
# [tool.uv]
# exclude-newer = "2024-09-22T00:00:00Z"
# ///
import importlib.metadata
from time import time
import anyio
import asks
URL = "https://pokeapi.co/api/v2/pokemon/"
async def fetch_pokemon(num: int):
r = await asks.get(f"{URL}{num}")
print(r.json()["name"])
async def main_poke():
start = time()
async with anyio.create_task_group() as tg:
for num in range(1, 151):
tg.start_soon(fetch_pokemon, num)
print(f"Time: {time() - start}s")
if __name__ == "__main__":
anyio.run(main_poke)
print(f"asks={importlib.metadata.version("asks")}") # ライブラリのバージョンを表示
print(f"anyio={importlib.metadata.version("anyio")}")
このスクリプトを実行すると、依存関係の解決とタイムスタンプの指定が行われます。タイムスタンプが指定されている場合、タイムスタンプより新しいバージョンはインストールされなくなります。「あの時は動いていたのに...」ということを回避しやすくなります。
$ uv run fetch.py
Reading inline script metadata from: fetch.py
ivysaur
...
kingler
Time: 1.3258390426635742s
asks=3.0.0
anyio=3.7.1
まとめ¶
8月の記事(バージョン1.0リリース記念:Rust製データフレームライブラリ、Polarsの進化した機能を試す | gihyo.jp)に続き、ライブラリのアップデート情報をお届けしました。
わたし自身、2024年5月に別のプロジェクト管理ツール「Hatch」を紹介しましたが、「uv」の進化を見ていると、Pythonの開発環境がどんどん進化し管理しやすくなっていると感じました。
今回は紹介しませんでしたが、 uv pip
も強化されており、個人的には uv pip compile
でプラットフォームごとの出力やPythonバージョンの指定が有効になった点が個人的に少し嬉しかったです。また、tox
を利用している場合は、 tox-dev/tox-uv を使うことで影響範囲を最小限に抑えながらCIを高速化できますので、ぜひ試してみてください。
高速なPython linterの「Ruff」から始まった「high-performance developer tools」の提供をミッションに掲げるAstral社は、その信念にあるように「We believe that a great tool can have an outsized impact.」の実現を実感できるツールを提供しています。
ぜひ、みなさまも「uv」をお試しください。