はじめに
今回は最近話題になっている Python のパッケージマネージャー uv を使用してみた感想と、Python のタスクランナーである invoke との相性についてまとめてみたいと思います。
筆者は Python を 4〜5 年ほど使用しており、仮想環境の管理で悩んだ経験があります。
そんな中で登場した uv はとても魅力的なツールだったため、実際にプロジェクトに導入して検証しました。
結論: とても使いやすくて超快適! でもinvokeとの相性は🤔??
パッケージインストールが圧倒的に早い(pip の 100 倍との記事も)
毎回環境が再生成されても Docker より全然速くて気にならない
Python 開発において困ることはほとんどありませんでした。
ただし、ひとつ課題がありました。
Python のタスクランナーである invoke との相性が悪い
そもそもinvokeとは?
invoke は Python 製のタスクランナーで、tasks.py にタスクをまとめて
inv server のように簡潔なコマンドで実行できます。
以下は筆者が普段使っているタスクの例です。
import os
from invoke.tasks import task
def get_env(extra_env: dict = {}) -> dict:
path = os.path.abspath(os.path.dirname(__file__))
env = {"PYTHONPATH": f"{os.environ.get('PYTHONPATH', '')}:{path}"}
if extra_env:
env.update(extra_env)
return env
@task
def run(c, filename):
c.run(f"python {filename}", env=get_env({"PYTHONUNBUFFERED": "1"}))
@task
def server(c):
path = os.path.abspath(os.path.dirname(__file__))
env = {"PYTHONPATH": f"{os.environ.get('PYTHONPATH', '')}:{path}"}
c.run("uvicorn main:app --reload", env=env)
inv serverと打つだけで FastAPI を環境付きで起動できるため、非常に便利です。
何が問題なのか?(uvとの相性)
uv では Python を実行する際に
uv run main.py
というように、必ず uv run が必要になります。
そのため invoke から実行すると、
uv run inv run main.py
という冗長なコマンドになってしまい、
invoke の便利さ・簡潔さが一気に失われてしまうのが最大の課題です。
解決策①: invoke のタスク内で uv を直接呼び出す
from invoke import task
@task
def run(c, filename):
c.run(f"uv run {filename}", env=get_env({"PYTHONUNBUFFERED": "1"}))
メリット
- invokeの仕組みをそのまま流用できる
デメリット - グローバル環境にinvokeをインストールする必要があり、プロジェクトごとに切り分けるのが難しい
- コマンドが二重構造になり、汚く感じる
解決策②: Makefileでuvをラップする(おすすめ)
.PHONY: run
run:
PYTHONPATH=$(PWD) uv run $(FILE)
メリット
- MakefileはPythonに依存しないため構造がシンプル
- uvとinvokeの依存関係が安全に分離される
デメリット
- Makefileの管理が増える
- Pythonコードではなくシェルコマンドで記述しないといけない
どちらが良いのか?
筆者としては Makefile の利用を強くおすすめします。
その理由は:
invoke 内で uv をラップすると、
invoke → uv → invoke という依存の入れ子構造になり気持ち悪い
invoke の存在意義(簡潔さ)が薄れる
Makefile なら Python とツールチェーンを綺麗に分離できる
uv の登場によって、
「タスク管理は Python の外側に置く方が合理的」という結論になりました。
さいごに
今回は uv と invoke の相性についてまとめてみました。
すでに invoke でタスク管理している環境がある場合は、これまでどおり invoke を使いつつ内部で uv を呼び出す構成にするのも良い選択肢です。
しかし、新規プロジェクトの立ち上げや、これからタスクランナーを導入する場合には、
Python に依存しない Makefile を採用することを強くおすすめします。
今回のようにニッチではあるものの、刺さる人には刺さる(と思っている)技術記事をこれからも執筆していく予定です。
2025年のアドベントカレンダーももう少し頑張っていきます。