Rye × uvでPython環境と機械学習環境を整える
チューリングのE2E自動運転チームの岩政(@colum2131)です。
最近、チーム内でPythonを使った開発はRyeとuvを使うことが多くなり、特に機械学習環境もRyeとuvで問題なく開発できるようになりました。社内でのオンボーディング資料としてRyeとuvの操作を整備しようと思い、このテックブログで紹介します。
[2024-08-25追記]
2024年8月20日にuvのマイナーバージョンがアップデートされました。変更点もいくつかあり、以下のページにまとめました。
1. Rye × uvとは?
RyeはPythonの包括的なプロジェクトおよびパッケージ管理のツールです。これまでもPoetryなど管理ツールはありましたが、pyenvなどPythonのバージョン管理ツールが必要でした。Ryeは、Pythonのバージョン管理からパッケージ管理を行えて、Poetry同様にpyproject.tomlの設定ファイルを使用したプロジェクト管理も可能です。
uvは非常に高速なパッケージインストーラおよびリゾルバーで、一般的なpipおよびpip-toolsの代替としてデザインされています。2024年2月からRyeからuvを使えるようになり、パッケージインストールの高速さから絶賛の声が相次ぎました。
一方で、RyeはPythonのパッケージ管理の最終的なソリューションになりそうにないとRye作者のArmin Ronacherはブログに綴っています。現在、Ryeの管理はuvの開発を行なっているAstralに移行されています。将来的には、uvがRyeに代替されるよう、現在も日々多くの機能がuvサポートされるようになっています。そのため、2024年7月現在でも一部OSSでRyeではなくuvだけが採用されているケースも見られます。
本ブログでは、現時点でのRyeおよびuv単体の基本的な操作や、便利な機能、CUDA依存のパッケージをインストールする方法を紹介します。
1.1 Ryeのインストール
Ryeの公式ドキュメントはRyeで整備されています。
今回はLinuxベースのOSでインストールします。それ以外のOSであれば公式のInstallation - Rye参照してインストールしてください。
Ryeのインストールは以下のコマンドで実行できます。途中、パッケージインストーラーをuvに選択して、RyeのPATHを.profileに追加するに選択してください。
curl -sSf https://rye.astral.sh/get | bash
もし.profileを参照しない環境であれば.bashrcなどに別途PATHを追加してください。
echo 'source "$HOME/.rye/env"' >> ~/.bashrc
今回使用するRyeのバージョンは以下になります。
rye --version
> rye 0.36.0
> commit: 0.36.0 (12c024c7c 2024-07-07)
> platform: linux (x86_64)
> self-python: cpython@3.12.3
> symlink support: true
> uv enabled: true
1.2 uv単体のインストール
uvのドキュメントはGitHubのREADME上に整備されています。
インストール方法はGetting Startedに記載されています。今回はpipxでインストールします。
pipx install uv
~/.bashrcに. "$HOME/.cargo/env"が追加されているのを確認し、設定を反映させます。
source ~/.bashrc
今回使用するuvのバージョンは以下になります。
uv version
> uv 0.2.24
2. Ryeの基本操作
公式のBasics - Ryeに従いましょう。以下、特によく使う基本操作です。
2.1 Ryeの新規プロジェクトの初期化
以下のコマンドで新規プロジェクトを作成することが可能です。my-projectは任意のプロジェクト名です。
 rye init my-project
 cd my-project
すると以下のディレクトリ構造が作成されます。
.
├── .git
├── .gitignore
├── .python-version
├── README.md
├── pyproject.toml
└── src
    └── my_project
        └── __init__.py
既に作成したいプロジェクト下にいる場合はrye initで作成できます。
2.2 RyeによるPythonバージョン変更
初期化時点で.python-versionファイルがデフォルトのPythonバージョンで書き込まれています。このプロジェクト内のPythonバージョンを変える場合はrye pinで変更することが可能です。以下、Python3.11にバージョンを変更します。
rye pin 3.11
2.3 Ryeによるパッケージの同期
pyproject.tomlをもとにパッケージをインストールします。以下のコマンドを実行できます。
rye sync
この処理の際、仮想環境として.venvが作成されます。また、requirements.lockやrequirements-dev.lockのlockfileも作成されて、Ryeの環境以外でもrequirements.lockなどを用いて必要なパッケージをインストールすることができます。
2.4 仮想環境のアクティベート
以下のコマンドで仮想環境を有効化できます。
. .venv/bin/activate
無効化する場合は以下を実行します。
deactivate
2.5 RyeによるPythonパッケージの追加
Ryeではrye addで新しくパッケージを追加することが可能です。高速なPythonのリンターおよびコードフォーマッターであるruffを追加してみます。
rye add ruff
この際、pyproject.tomlのdependenciesが更新されるのが確認できます。
dependencies = [
    "ruff>=0.5.1",
]
追加しているパッケージはrye listから確認することも可能です。
rye list
> -e file:///workspaces/my-project
> ruff==0.5.1
パッケージの追加はバージョンを指定することも可能です。
rye add "numpy<=1.26.4"
また、rye removeでパッケージを取り除くことも可能です。
rye remove numpy
パッケージの追加はrye add --devで開発用として追加することも可能です。
rye add numpy --dev
[tool.rye]に追加されるのが確認でき、lockfileはrequirements-dev.lockのみ更新されます。
[tool.rye]
managed = true
dev-dependencies = [
    "numpy>=2.0.0",
]
2.6 Ryeによる実行
rye runで実行ファイルを実行することができます。例えば、ruffによるリンターの実行であれば以下で実行します。
rye run ruff check
同様に仮想環境を有効化せずともrye run pythonで特定のPythonスクリプトを実行することも可能です。
3. uvの基本操作
uvの基本的な操作はREADMEに書かれていますが、全ての操作が書かれているわけではなさそうです。もし整備されたドキュメントがあれば教えてもらえると助かります。
3.1 uvで仮想環境を作成してパッケージをインストールする
uvであれば、仮想環境は以下のコマンドで作成できます。
uv venv
uvでPythonのバージョンを変更する場合はuv pythonで操作が行えます。uv python pinでPythonバージョンを指定した後にuv python installでPythonをインストールできます。
uv python pin 3.11
uv python install
仮想環境の有効化や無効化は前述通りの操作です。
. .venv/bin/activate
deactivate
uvで新しくパッケージをインストールする処理はuv pip installで実行できます。以下でruffをインストールします。
uv pip install ruff
インストールしたパッケージはuv pip listから確認することができます。
uv pip list
 3.2 pyproject.tomlを用いてuvを操作する
uvもpyproject.tomlに対応しており、Pythonプロジェクトの構成情報をまとめて記述できて利便性が非常に高いです。現在は、pyproject.tomlを生成するコマンドはuvに存在しないためプロジェクト下に以下のファイルを作成します。
[project]
name = "my-package"
version = "0.1.0"
description = "My package description"
authors = []
requires-python = ">=3.10"
pyproject.tomlが存在する場合はuv addでパッケージを追加することが可能です。以下でruffをインストールします。
uv add ruff
するとpyproject.tomlにdependenciesが追加されます。
[project]
name = "my-package"
version = "0.1.0"
description = "My package description"
authors = []
requires-python = ">=3.10"
dependencies = [
    "ruff",
]
バージョンを指定することも可能です。例えばnumpyのversion1.26.4をインストールする場合は以下になり、また開発用としてインストールすることができます。
uv add "numpy==1.26.4" --dev
dependenciesは以下のようになります。
[project]
name = "my-package"
version = "0.1.0"
description = "My package description"
authors = []
requires-python = ">=3.10"
dependencies = [
    "ruff",
]
[tool.uv]
dev-dependencies = [
    "numpy==1.26.4",
]
また、このpyproject.tomlにdependenciesが存在すればuv syncで同期することができます。
uv sync
特定のパッケージを取り除く場合はuv removeを使います。開発用としてインストールしたパッケージは--devをつけます。
uv remove numpy --dev
4. Ryeの便利な機能
 4.1 [tool.rye.scripts]によるタスクランナー
pyproject.toml内の[tool.rye.scripts]で、rye runを介して扱えるカスタムスクリプトを登録することができます。これを設定することで、例えばrye run lintを設定して、複数のリンターをこのコマンドのみで実行することができます。
最初に必要なパッケージを追加します。
rye add ruff mypy pytest pytest-cov
pyproject.tomlに以下を追加します。chainを使うことで複数のスクリプトを1回で実行することが可能になります。
[tool.rye.scripts]
lint = { chain = ["lint:ruff", "lint:ruff_format", "lint:mypy" ] }
"lint:ruff" = "ruff check ./ --diff"
"lint:ruff_format" = "ruff format --check --diff"
"lint:mypy" = "mypy ./ --explicit-package-bases"
以上を追記後、以下を実行するとruffとmypyが実行されます。
rye run lint
同様にruffによるフォーマッターも[tool.rye.scripts]に以下を追加します
format = { chain = [ "format:ruff", "format:ruff_check" ] }
"format:ruff" = "ruff format ./"
"format:ruff_check" = "ruff check ./ --fix"
以下を実行するとフォーマッターが実行されます。
rye run format
また、chainを使わない場合は{ cmd = "..." }やそのまま文字列として書くことも可能です。
test = { cmd = "pytest ./tests --cov=./src --cov-report term-missing --durations 5" }
実際にpytestをテストしてみる場合はtests/test_init.pyに以下のコードを書いて、
from my_project import hello
def test_hello() -> None:
    assert hello() == "Hello from my-project!"
pyproject.tomlの[tool.pytest.ini_options]を新しく追加してください。テスト対象のプロジェクトのpythonpathやテストコードが存在するパスを設定できます。
[tool.pytest.ini_options]
pythonpath = "src"
testpaths = ["tests"]
以上を追記後、以下を実行するとpytestが実行されます。
rye run test
以上が最終的な[tool.rye.scripts]です。GitHub Actionsで実行する用にlint_githubを追加しています。
[tool.rye.scripts]
lint = { chain = ["lint:ruff", "lint:ruff_format", "lint:mypy" ] }
"lint:ruff" = "ruff check ./ --diff"
"lint:ruff_format" = "ruff format --check --diff"
"lint:mypy" = "mypy ./ --explicit-package-bases"
format = { chain = [ "format:ruff", "format:ruff_check" ] }
"format:ruff" = "ruff format ./"
"format:ruff_check" = "ruff check ./ --fix"
test = { cmd = "pytest ./tests --cov=./src --cov-report term-missing --durations 5" }
lint_github = { chain = [ "lint_github:ruff", "lint:ruff_format", "lint:mypy" ] }
"lint_github:ruff" = "ruff check ./ --diff --output-format=github"
4.2 ruffやmypyの設定
2024年7月現在、PythonのCIにおいてruffの流行りを感じます。ruffは非常に高速なリンターおよびフォーマッターであり、Flake8やBlack、isort、pydocstyle、pyupgrade、 autoflakeなどを置き換えることが可能なツールになっています。また、mypyはPythonで実行可能な静的型チェッカーであり、型注釈によるコードの可読性の向上などコードの品質を高めることができます。
これらの設定はpyproject.tomlに記載することができます。コミュニティやプロジェクトごとに依存している設定があると思いますが一例を紹介します。
ruffの場合は@hppさんのこちらのポストを参考に設定しています。
[tool.ruff]
exclude = [".git", ".mypy_cache", ".ruff_cache", ".venv", "third_party"]
line-length = 160
target-version = "py311"
[tool.ruff.lint]
fixable = ["ALL"]
unfixable = []
select = [
    "A",  # flake8-builtin
    "B",  # flake8-bugbear
    "E",  # pycodestyle error
    "F",  # Pyflakes
    "I",  # isort
    "N",  # pep8-naming
    "W",  # pycodestyle warning
    "PL",  # Pylint
    "UP",  # pyupgrade
]
ignore = [
    "B905",  # Zip-without-explicit-strict
    "E501",  # Line too long
    "F403",  # Unable to detect undefined names
    "N812",  # Lowercase imported as non-lowercase
    "N999",  # Invalid module name
    "PLR0912",  # Too many branches
    "PLR0913",  # Too many arguments in function definition
    "PLR2004",  # Magic value used in comparison
]
[tool.ruff.format]
quote-style = "double"
line-ending = "auto"
mypyの場合はCADDiのテックブログのmypy 設定ファイルの読み合わせと修正を実施しましたを参考に設定しています。
[tool.mypy]
allow_redefinition = true
allow_untyped_globals = false
check_untyped_defs = true
color_output = true
disallow_incomplete_defs = true
disallow_subclassing_any = false
disallow_untyped_calls = false
disallow_untyped_decorators = false
disallow_untyped_defs = true
error_summary = true
ignore_missing_imports = true
implicit_reexport = true
namespace_packages = true
no_implicit_optional = true
pretty = true
show_column_numbers = true
show_error_codes = true
show_error_context = true
show_traceback = true
strict = true
warn_no_return = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
warn_unused_ignores = false
この記事ではruffやmypyの詳細は紹介しませんが、ruffであれば公式ドキュメントのRules - ruffを参照すると良いです。
 4.3 [tool.hatch.build.targets.wheel]によるモジュールアクセス
Poetryであれば[tool.poetry]のpackagesと近いことができる設定です(本来の機能と異なるかもしれませんが…)。以下のように設定することで、ディレクトリ構造を気にすることなくモジュールをimportすることが可能になります。
[tool.hatch.build.targets.wheel]
packages = ["src/my_project", "src"]
4.4 GitHub Actions上でRyeをインストールする
GitHub Actions上でCIを行うときに、Ryeのインストールおよびタスクランナーでリンターを実行することができます。Ryeのインストール後にecho "$HOME/.rye/shims" >> $GITHUB_PATHでパスを追加することが必要です。
name: Run Linters
on:
  pull_request:
    types: [ opened, synchronize ]
    paths:
      - 'src/**'
      - 'tests/**'
      - 'pyproject.toml'
      - 'requirements-dev.lock'
      - 'requirements.lock'
      - '.github/workflows/linter.yml'
jobs:
  linters:
    runs-on: ubuntu-22.04
    strategy:
      matrix:
        python-version: [3.11]
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install rye
        run: |
          curl -sSf https://rye.astral.sh/get | RYE_INSTALL_OPTION="--yes" bash
          echo "$HOME/.rye/shims" >> $GITHUB_PATH
      - name: Install dependencies
        run: rye sync
      - name: Lint
        run: rye run lint_github
5. RyeでCUDA依存のパッケージをインストールする
[[tool.rye.sources]]によってPyPI以外のindexからパッケージを追加することができます。特に機械学習ライブラリは独自のインストールインデックスが指定されるものが多く、こちらを活用してインストールすることが多いです。
以下にtorch、mmcv、cupy、RAPIDS系(cudf、cuml)の追加方法を記載します。その他のCUDA依存のパッケージの追加ができたら追記していきます。
5.1 torchの場合
PyTorchのprevious-versionsを参照し、インストールするtorchのversionとその--index-urlを調べます。
今回はversion2.1.2でCUDAは12.1依存のPyTorchをインストールします。最初に以下をpyproject.tomlに追加します。
[[tool.rye.sources]]
name = "torch"
url = "https://download.pytorch.org/whl/cu121"
type = "index"
そして以下のコマンドでCUDA依存のtorchをインストールできます。
rye add "torch==2.1.2+cu121"
同様にtorchvisionもtorchaudioもインストールできます。
 rye add "torchvision==0.16.2+cu121"
 rye add "torchaudio==2.1.2+cu121"
5.2 wheelからインストール
CUDAのバージョンを合わせるために自前でビルドした場合など、localのwheelを追加したい場合はrye add コマンドの path オプションで追加可能です。
ただしフルパスを記載する必要があるため、コンテナ環境でパスを固定するなどを併用する必要があります。
Can't add relative path dependencies in virtual project
rye add --path /path/to/xxx.whl
5.3 mmcvの場合
INSTALLATION - mmcvを参照して、インストールします。Install with pipを参照のもとmmcvの設定を作成します。
OSはLinuxで、cuda12.1で、torch 2.1.x系でmmcv 2.20をインストールする場合は以下のpipコマンドになり、
pip install mmcv==2.2.0 -f https://download.openmmlab.com/mmcv/dist/cu121/torch2.1/index.html
この設定から以下の設定を追記します。
[[tool.rye.sources]]
name = "mmcv"
url = "https://download.openmmlab.com/mmcv/dist/cu121/torch2.1/index.html"
type = "find-links"
そして以下のコマンドでmmcvをインストールできます。
 rye add "mmcv==2.2.0"
5.3 cupyの場合
cupyの場合はそのままrye addすることができます。CUDA11系の場合はcupy-cuda11x、CUDA12系の場合はcupy-cuda12xを指定します。
rye add cupy-cuda12x
5.4 RAPIDSの場合
RAPIDS Installation Guideをもとにpipコマンドを調べます。CUDA12系であれば以下であり、
pip install \
    --extra-index-url=https://pypi.nvidia.com \
    cudf-cu12==24.6.* dask-cudf-cu12==24.6.* cuml-cu12==24.6.* \
    cugraph-cu12==24.6.* cuspatial-cu12==24.6.* cuproj-cu12==24.6.* \
    cuxfilter-cu12==24.6.* cucim-cu12==24.6.* pylibraft-cu12==24.6.* \
    raft-dask-cu12==24.6.* cuvs-cu12==24.6.*
この設定から以下の設定を追記します。
[[tool.rye.sources]]
name = "RAPIDUS"
url = "https://pypi.nvidia.com"
type = "index"
そして以下のコマンドでcudfとcumlをインストールできます。他のパッケージも同様にインストールできます。
rye add "cudf-cu12==24.6"
rye add "cuml-cu12==24.6"
6. uvでCUDA依存のパッケージをインストールする
pyproject.tomlでの書き方は変わりますが、Rye同様にインストールすることが可能です。
6.1 torchの場合
以下をpyproject.tomlに追記します。
[tool.uv.pip]
index-url = "https://download.pytorch.org/whl/cu121"
そして以下のコマンドでtorchのversion2.1.2をインストールできます。
uv add "torch==2.1.2"
 6.2 追加でindex-urlを追加する場合
index-urlは単一のURLしか指定できません。複数指定する場合は、extra-index-urlに記載します。RAPIDUSのcuDFを追加する場合は、pyproject.tomlに以下を追記します。
[tool.uv.pip]
index-url = "https://download.pytorch.org/whl/cu121"
extra-index-url = ["https://pypi.nvidia.com"]
そして以下コマンドでcuDFをインストールできます。
uv add "cudf-cu12==24.6"
7. 終わりに
Ryeとuvの他に便利な機能があれば追記していきます!快適なRye × uvライフを!







Discussion