72
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】uvで始めるPythonプロジェクト

Last updated at Posted at 2024-10-28

はじめに

Ryeで始めるPythonプロジェクトを執筆してから半年近く経ちました。
(よければ、まずRyeの記事から読んでいただけると嬉しいです)

この半年でuvが大きく成長し、Ryeと同等かそれ以上のプロジェクトおよびパッケージ管理ができるようになりました。そのため、今度はuvを使って開発を進めたい方向けの記事も上げてみようと思いました。
本稿はRyeの記事同様、ハンズオン形式で誰でも気軽にプロジェクトを作れるようになっています。また、Ryeとの違いについても簡単にですが、言及します。

ハンズオン環境

  • Ubuntu / MacOS
  • uv : 0.4.24

0. uvとは?

Rustで書かれた非常に高速なPythonパッケージとプロジェクト管理ツールです。

以前に、Ryeの記事を執筆した際には、「パッケージインストーラー(pip等)の代替」ような位置づけでuvを利用していました。実際にRyeでプロジェクト作成する際は、pipかuvでパッケージインストーラーを選ぶ形になっていました。

現在のuvはというと、下記のような機能を代替できます。

  • pipenv, poetry, virtualenvなど(パッケージ、プロジェクト管理ツール)の代替
  • pipの代替(10倍から100倍速い)
  • pyenv(バージョン管理ツール)の代替

Rye(0.41.0)とuv(0.4.24)を簡単に機能面で比較してみると、下記のような感じになります。

項目 uv Rye
パッケージ管理 pip に依存せず独自管理 pip(uvも選択可)を利用して管理
プロジェクト管理 uv initでプロジェクト作成 rye initでプロジェクト作成
pip との互換性 uv内のpip互換で依存関係を管理可能 pip(uv)と連携して依存関係を管理
依存関係の解決 独自の依存関係解決システム pip(uv)を通じて依存関係を解決
バージョン管理 uv pythonで管理 Toolchainで管理
バージョン固定 pinでバージョン固定可能 pinでバージョン固定可能
依存ファイル pyproject.tomlに記載 pyproject.tomlに記載
スクリプト実行 uv runコマンドで実行 rye runコマンドで実行
ロックファイル uv.lock で依存関係を固定 rye.lock で依存関係を固定

大きく差異はないですが、現状、Ryeからuvを使うのであればuvだけで良いでしょう。もし、pipを今後も利用したい場合は、Ryeを選ぶとよいでしょう。

注意点
ちなみに、uvからpipを操作することも出来ますが、「ベースとなっているツールのインターフェイスや動作を正確に実装しているわけではない」らしいので、Ryeを使う方が無難です。
(参考 : https://docs.astral.sh/uv/getting-started/features/#the-pip-interface)

# 例
uv pip install
uv pip list

1.インストール編

基本的には、公式の誘導に従います。
(Windows へのインストールは本稿では扱わないので公式見てね)

1.1. ダウンロードする

まず、curl コマンドを実行しましょう。適したバイナリファイルが落ちてきます。

curl -LsSf https://astral.sh/uv/install.sh | sh

pipからも落とすこともできますが、pip に依存する形を好まないので割愛します。Rye と異なり、 $HOME/.cargo/env ($HOME/.cargo/env.fish)に PATH が通っていれば良いようです。

# sh, bash, zsh
source $HOME/.cargo/env

# fish
source $HOME/.cargo/env.fish

1.2. Shell の補完スクリプトを入れる

rye と同様に shell 補完スクリプトが提供されているため、ついでに入れましょう。

# bash
echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc

# zsh
echo 'eval "$(uv generate-shell-completion zsh)"' >> ~/.zshrc

# fish
echo 'uv generate-shell-completion fish | source' >> ~/.config/fish/config.fish

1.3. uv 自体をアップデートする

最後に uv 自体をアップデートしましょう。

uv self update

2. Python管理編

uvでは、pyenvのように Python のバージョン管理をすることが可能です。
uvはRye同様、必要に応じて自動的にPythonのバージョンを取得するため、Pythonをインストールする必要はないですが、pyenvのように手動でインストールしたり、バージョン確認することが下記のように出来ます。

uv python install 3.13

一度に複数のバージョンをインストールすることも可能です。
(リビジョンまで指定可能です)

uv python install 3.11.7 3.12 3.13
# 結果
Searching for Python versions matching: Python 3.11.7
Searching for Python versions matching: Python 3.12
Searching for Python versions matching: Python 3.13
Installed 3 versions in 7.05s
   + cpython-3.11.7-macos-x86_64-none
   + cpython-3.12.7-macos-x86_64-none
   + cpython-3.13.0-macos-x86_64-none

インストールした Python は下記のように確認可能です。

uv python list

3. 新規プロジェクト作成編

3.1. プロジェクトを作成する

uv initを用いてプロジェクトを作成していきます。

uv init uv-sample-project
cd uv-sample-project

既にプロジェクトのフォルダがある場合は、下記でOKです。

cd uv-sample-project
uv init

そうすると、以下のようなフォルダ構成が出来上がります。

.
├── .python-version
├── README.md
├── hello.py
└── pyproject.toml

生成されたpyproject.tomlは下記のような感じになります。

[project]
name = "uv-sample-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

Ryeとの比較
Ryeの方が下記のように、デフォルトで色々と生成してくれます。簡単に動かすという観点ではuvよりも優れているかもしれません。ただし、プロジェクトを作り込んでいく場合は、uv位シンプルでいいかなと思います。

# フォルダ構成
.
├── .git
├── .gitignore
├── .python-version
├── README.md
├── pyproject.toml
└── src
    └── my_project
        └── __init__.py
# pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
authors = [
    { name = "kissy24", email = "hogehoge@fuga.com" }
]
dependencies = []
readme = "README.md"
requires-python = ">= 3.8"

[project.scripts]
hello = "my_project:hello"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.rye]
managed = true
dev-dependencies = []

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]

2.2. hello worldする

まず何かパッケージを入れたいですね。とりあえずRuffを入れてみましょうか。

uv add ruff

ライブラリを追加したら必ずuv syncしましょう…と言いたいのですが、uvはuv adduv.lock.venv爆速で更新される上にuv runを起動する前に、uv.lockpyproject.tomlが最新であること、uv.lock.venvが最新であることを爆速で確認してくれます。そのため、Ryeと異なりsyncの出番はほぼありません。(コマンドとしては存在します)

>>> uv run hello.py
Hello from uv-sample-project!

uvで無事プロジェクトを作ることができました。Ryeより考えることは少ないですね。

2.3. パッケージを開発環境にだけ入れる

おっと、Ruffは開発でしか必要の無いライブラリでしたね。
こういうときはいつも通り、devパッケージとしてインストールしましょう。

# 一旦ruffを削除します
uv remove ruff
# devパッケージとしていれます
uv add --dev ruff
# 実行用途のみで使う場合
uv sync --no-dev

はい、ちょっと待ってください。さっき「syncの出番はほぼありませんといったそばからuv sync使ってません?」と思ったそこのあなた、センスがいいです。
uvではdevelop環境で利用するtoolをuv tool(uvx)というコマンド別管理することが出来ます。
(.venvにRuffを含まず、Ruffが利用できます。保存先はuv tool dirで確認できます)

# 一旦devパッケージからruffを削除します
uv remove --dev ruff
# toolとしてruffをいれます
uv tool install ruff

では、実際に動くのか見てみましょうか。

>>> uv tool run ruff check hello.py
All checks passed!

コマンドによるRuffのチェックが出来ました。ちなみにパッケージを削除・更新する場合は下記のコマンドより可能です。

# 更新
uv tool upgrade ruff
# 削除
uv tool uninstall ruff

更に面白いのは、ruffをインストールしなくてもuvxコマンドでツール実行ができます。
(ちなみに uvxuv tool run のエイリアスです)

>>> uvx ruff check hello.py
All checks passed!

開発時のパッケージの持ち方は、下記のように棲み分けすると良さそうですね。

  • 開発時の環境に制約をかけたい場合はuv add --dev
  • ローカルにパッケージを持っておきたい場合はuv tool install
  • 特に制約のない場合はuvx

3.3. おまけ : uvxでPytestを実行する

Ruffと同じ方法でPytestも出来るのか実際にやってみましょう。
まず、ソースコードを下記のように変更します。

hello.py
def hello() -> str:
    return "Hello from uv-sample-project!"


def main():
    print(hello())


if __name__ == "__main__":
    main()

次にテストコードを作成しましょう。(hello.pyと同階層でOKです)

test_hello.py
import hello


def test_hello():
    assert hello.hello() == "Hello from uv-sample-project!"

これで、準備ができたのでPytestしてみましょう。

>>> uvx pytest      

platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
rootdir: /home/hoge/workspace/uv-sample-project
configfile: pyproject.toml
collected 1 item        

test_hello.py .   

無事に動作しましたね。

次に、pytest-covが使えるか見ていきましょうか。

※ 2024/11/23 追記

withオプションを利用することで依存関係を含めて実行することが出来ます。
(教えていただき、ありがとうございました!)

https://docs.astral.sh/uv/guides/tools/#commands-with-plugins

$ uvx --with pytest-cov pytest --cov

Installed 6 packages in 7ms
================================================================ test session starts =================================================================
platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0
rootdir: /home/kissy24/workspace/uv-sample-project
configfile: pyproject.toml
plugins: cov-6.0.0
collected 1 item

test_hello.py .                                                                                                                                [100%]

---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name            Stmts   Miss  Cover
-----------------------------------
hello.py            6      2    67%
test_hello.py       3      0   100%
-----------------------------------
TOTAL               9      2    78%

uv tool installも問題なくできるようです。

$ uv tool install pytest --with pytest-cov

Resolved 6 packages in 78ms
Installed 2 packages in 5ms
 + coverage==7.6.7
 + pytest-cov==6.0.0
Installed 2 executables: py.test, pytest

pytest-mock 等の依存関係も同様になります。

※ 以下は、withオプションを利用しない場合の手段になります。

$ uv add --dev pytest-cov

Resolved 8 packages in 80ms
Prepared 6 packages in 114ms
Installed 6 packages in 7ms
 + coverage==7.6.4
 + iniconfig==2.0.0
 + packaging==24.1
 + pluggy==1.5.0
 + pytest==8.3.3
 + pytest-cov==5.0.0

$ uv run pytest --cov -v

platform linux -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 -- /home/hoge/workspace/uv-sample-project/.venv/bin/python3
cachedir: .pytest_cache
rootdir: /home/hoge/workspace/uv-sample-project
configfile: pyproject.toml
plugins: cov-5.0.0
collected 1 item                  

test_hello.py::test_hello PASSED   [100%]

---------- coverage: platform linux, python 3.13.0-final-0 -----------
Name            Stmts   Miss  Cover
-----------------------------------
hello.py            6      2    67%
test_hello.py       3      0   100%
-----------------------------------
TOTAL               9      2    78%

ここまでが、ハンズオンになります。下記に、ハンズオンのコードを置いてますのでよければ確認してみてください。

おわりに

いかがでしたでしょうか?個人的にuvを触っていて思ったのは、VSCodeのPython補完とuvxの相性が良いと感じました。例えば、VSCode Ruffであれば、

Ruff v0.4.5以降、RuffにはRustで書かれた組み込み言語サーバーが搭載されています: ⚡ ruff server ⚡ このサーバーはRuff v0.5.3で安定版としてマークされ、利用可能であれば拡張機能で自動的に使用されます。 参照してください: Rustベースの言語サーバーを有効にする
(参考 : https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)

となっており、Ruffに限らず開発者ツールはプロジェクト外(エディタや統合管理ツール)で管理されるような流れを感じます。おそらく、今後のPythonプロジェクトはRustやGoのように開発者がlinterやformatterを気にしなくても、デファクト・スタンダードのようなものが勝手についてくるようになるのではと感じています。

uvは、コードに向き合う時間を増やしてくれるような可能性を感じさせてくれました。Rye、uv問わずこの手の統合管理ツールがより盛り上がってくることを願っています。

72
62
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
72
62

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?