モダンなPython開発環境の技術スタック 【uv / Ruff / ty / Taskfile etc.】
はじめに
この記事はサイバーエージェント26卒内定者 Advent Calendar 3日目の記事です。
昨今の生成AIブームも相まって、Pythonの需要はますます高まっています[1]。
幸いなことに、uv・Ruff・ty といった高速で便利なRust製ツールが充実してきたおかげで、Pythonの開発環境構築は以前よりも格段に容易になりました。
ここ数年はツールの進化が激しく、自分自身も試行錯誤を繰り返してきましたが、最近ようやく構成が安定してきました。
この記事では、自分が現在使っている Python開発環境の概要 や、その他のTipsをご紹介します。
詳細な使い方については、各ツールの公式ドキュメントを参照してください。
ディレクトリ構成
以下のようなsrc-layoutで作成したPythonプロジェクトを想定しています。
uvのコミュニティでsrc-layoutが推奨されている・開発中のコードの誤使用を抑制するといった観点からsrc-layoutを使うことが多いです。
テストコードに関してはSansanさんの研修資料をベースにsrc配下とtests配下が対応するように配置しています。
.
├─ .vscode
│├─ launch.json
│└─ settings.json
├─ .gitignore
├─ .pre-commit-config.yaml
├─ .python-version
├─ pyproject.toml
├─ README.md
├─ Taskfile.yaml
├─ uv.lock
├─ src
│└─ package_name
│ ├─ __init__.py
│ └─ main.py
└─ tests
├─ package_name
│└─ test_main.py
└─ conftest.py
技術スタック
【uv】 パッケージ管理
パッケージ管理ツールにはuvを使用しています。
uvはAstral社が開発しているRust製のPython用のパッケージ管理ツールで、pipなどの他のツールと比較して、極めて高速に動作することが特徴です。
また、pipやPoetryなどのパッケージのみを管理するツールと異なり、Python自体のバージョンも管理できるため、環境構築が非常に容易になります。
デメリットとして、まだ登場してから日が浅いためか、uvを使用している公開レポジトリが少ないことが挙げられます。特に、研究の著者実装などはpipやcondaを使用していることが多く、環境構築に苦労する場合があります。(uvがもっと普及してくれれば嬉しいなと思っています)

ライブラリ「Trio」をインストールした場合の実行速度比較 (uv公式)
| ツール | GitHubスター[2] | Python自体のバージョン管理 | パッケージ管理 (Python) | パッケージ管理 (Python以外) | 速度 | 備考 |
|---|---|---|---|---|---|---|
| uv | 74k | ○ | ○ | - | ◎ | - |
| rye | 14.3k | ○ | ○ | - | ○ | uvの前身となるツール |
| Poetry | 34.1k | - | ○ | - | △ | - |
| conda | 7.2k | ○ | ○ | ○ | × | 唯一Python以外のパッケージ(CUDAなど)を管理できる |
| pip | 10k | - | ○ | - | △ | 標準 |
| venv | - | - | ○ | - | △ | 標準 |
| (pyenv) | 43.8k | ○ | - | - | △ | Python自体のバージョン管理のみ |
機械学習の用途であればPytorchなどのCUDAに依存するライブラリをどのようにインストールすれば良いのかという課題があります。
自分の場合、以下のようにpyproject.tomlを手動で編集することにより、適切なバージョンのPytorchをインストールするように設定しています。
[project]
name = "sample_project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.9.19"
dependencies = [
"torch==2.1.2", # Editable
]
[tool.uv.sources]
torch = [
{ index = "pytorch-cuda", marker = "platform_system == 'Linux'" },
]
[[tool.uv.index]]
name = "pytorch-cuda"
url = "https://download.pytorch.org/whl/cu121" # Editable
explicit = true
cu121の部分は現在のCUDAのバージョンによって変更しています。
CUDA・Python・Pytorchのバージョン要件を満たすバイナリがあるかどうかは、以下のサイトからcontrol+Fで手動で探してきています。
同様に、Pytorch本体とtorchvision・torchaudioの互換性については、以下のサイトから確認しています。
より詳細については、ころんびあさんの記事やもりりんさんのスクラップなどが参考になります。
【Ruff】 リンター・フォーマッター
リンター・フォーマッターにはRuffを使用しています。
Ruffは静的解析を行うリンターと、コードの整形を行うフォーマッターを兼任するツールです。
uvと同様にAstral社が開発しているRust製のツールで、従来のツールと比較して極めて高速に動作します。公式でPlaygroundが用意されています。

スクラッチ開発したCPythonにリンターをかけた場合の実行速度比較 (Ruff公式)
| ツール | GitHubスター[2:1] | リンター | フォーマッター | 速度 | 備考 |
|---|---|---|---|---|---|
| Ruff | 44.1k | ○ | ○ | ◎ | - |
| flake8 | 3.7k | ○ | - | △ | PyFlakes, mccabe, pycodestyleを統合したツール |
| Pylint | 5.6k | ○ | - | × | - |
| autopep8 | 4.7k | - | ○ | △ | - |
| yapf | 14k | - | ○ | △ | - |
| black | 41.2k | - | ○ | △ | - |
| isort | 6.9k | - | ○ | △ |
importの整形のみ |
Ruffの設定はpyproject.tomlで一元管理しており、紆余曲折あり以下の形に落ち着きました。
line-lengthは標準の80文字だと短すぎるため、120文字に設定しています。
force-single-line=trueは、1行で1つのオブジェクトしかimportできないようにする設定です。マージコンフリクトを避ける観点から設定しています。
[tool.ruff]
exclude = [
".git",
".mypy_cache",
".ruff_cache",
".import_linter_cache",
".venv",
"third_party",
]
line-length = 120
[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
"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.lint.isort]
force-single-line = true
[tool.ruff.format]
quote-style = "double"
line-ending = "auto"
VSCodeには公式の拡張機能があります。この拡張機能をインストールすると、VSCodeのエディター内でruffのリンター・フォーマッターを自動で適用できるようになります。
VSCodeの設定ファイル.vscode/settings.jsonで"editor.defaultFormatter": "charliermarsh.ruff"と設定すると、VSCode側がruffを認識します。
この設定を行うことで、コードを書いている際にエラー箇所を静的に解析し、詳細を表示してくれるようになります。
また、"editor.formatOnSave": trueと設定すると、ファイル保存時に自動でフォーマットが適用されます。
{
"[python]": {
"editor.formatOnSave": true,
"editor.formatOnPaste": false,
"editor.formatOnType": false,
"editor.formatOnSaveMode": "file",
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
}
},
}
【ty / mypy】 型チェック
型チェックにはtyを使用しています。
tyはAstral社が開発しているRust製のPython用の型チェックツールです。ruffと同様に極めて高速に動作します。
まだプレビュー版になりますが、開発元がAstral社なので、
- 今後も継続的にメンテナンスが行われると思われる
- uv・ruffとの相性が良くなると見込まれる
- 既に人気が高い (GitHubスター 13.7k)
という点から先行してtyを選択しています。
| ツール | GitHubスター[2:2] | ドキュメント量 | 速度 | 備考 |
|---|---|---|---|---|
| ty | 13.7k | △ | ◎ | プレビュー版 |
| mypy | 20k | ○ | △ | おそらくデファクトスタンダード |
| pyright | 15k | ○ | - | - |
| pyre-check | 7.1k | △ | - | - |
| pyrefly | 4.9k | △ | ◎? | プレビュー版? |
tyにも公式のVSCode拡張機能があります。特に設定は不要で、変数定義などの際に型を表示してくれるようになり便利です。
mypyとの比較についてはちくわぶさんの記事が参考になります。
【import-linter】 モジュールの依存性チェック
モジュールの依存性を制御するためのツールとしてimport-linterを使用しています。
開発にクリーンアーキテクチャやオニオンアーキテクチャなどの思想を取り入れる場合には、モジュール間の依存性の制御が重要になります。
Cursorなどのコード生成を利用すると、稀に依存のルールを無視してしまい、目視でも確認が漏れてしまうケースがありました。
そこで、import-linterを使って依存性の制御を厳格にチェックするようにしています。
例えば、pyproject.tomlに
[tool.importlinter]
root_packages = ["sample_project"]
[[tool.importlinter.contracts]]
name = "Layered architecture contract"
type = "layers"
layers = [
"sample_project.infrastructure",
"sample_project.adapters",
"sample_project.use_cases",
"sample_project.entities",
]
のように設定し、
uv run import-linter check .
を実行すると、インフラストラクチャ層からエンティティ層の方向のみに依存しているかをチェックしてくれます。
【Taskfile / Makefile】 ビルドツール・タスクランナー
ビルドツール/タスクランナーにはTaskfileまたはMakefileを使用しています。
Makefileはソフトウェアのビルドを自動化する設定ファイルで、以下のようにMakefileで処理を自動化することができます。
.PHONY: install
install:
uv sync
.PHONY: test
test:
uv run pytest
.PHONY: check
check:
uv run ruff check .
uv run ruff format .
uv run ty check .
uv run lint-imports
.PHONY: run
run:
uv run python main.py
しかし、Makefileには以下のようなデメリットがあります。
- あくまで生成物を伴う「ビルド」を管理するツールであり、生成物がない「タスク」を管理するには特殊な記法(
.PHONY)が必要で不自然 - 複雑な処理を記述すると可読性が低下する
- ドキュメントが読みづらい
そこで自分の場合はTaskfileを好んで使用しています。Homebrewなら以下のようにダウンロードできます。
brew install go-task/tap/go-task
先ほどの例と同等の処理をTaskfile.yamlで書くと以下のようになります。
version: "3"
tasks:
default:
desc: "Default task"
cmds:
- task --list
install:
desc: "Install dependencies"
cmds:
- uv sync
test:
desc: "Run tests"
cmds:
- uv run pytest
check:
desc: "Run static analysis"
cmds:
- uv run ruff check .
- uv run ruff format .
- uv run ty check .
- uv run lint-imports
run:
desc: "Run local server"
cmds:
- uv run python main.py
Taskfileは
- 自然にタスクを管理できる
- YAMLで記述するため可読性が高い
- ドキュメントが非常に読みやすく充実している
という点で気に入っています。一方で、Taskfileは (1)インストールが必要 (2)知名度がやや低いというデメリットがあるので、状況を見て使うようにしています。
MakefileとTaskfileには、両方ともVSCodeの拡張機能があります。
サイドバーから処理を実行できるようになり便利なので、是非インストールしてみてください。
【pre-commit】 コミット前の静的解析
pre-commitを使用して、コミット前に静的解析を行うようにしています。
pre-commitはGitのpre-commit hookを管理できるツールで、commitコマンドを実行する前にリンター・フォーマッター・型チェッカーでコードをチェックすることができます。
コミット前にmakeやtaskでコードを確認するのを忘れがちなので、コード品質が重要な場合にはpre-commitを使っています。
.pre-commit-config.yamlは以下のように設定していることが多いです。
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.6
hooks:
- id: ruff-check
- id: ruff-format
- repo: local
hooks:
- id: ty
name: ty check
entry: uv run ty check . --ignore unresolved-import
language: python
Ruffには公式のpre-commit hookを使用しています。
tyには公式のhookがないので、issueにあったローカルコマンドを呼び出す方法により実行しています。近々公式のhookも追加されるのではないかと思います。
【gibo】 .gitignoreの自動生成
.gitignoreファイルを生成するツールとしてgiboを使用しています。
機密情報が含まれる.envファイルやキャッシュファイルなどをコミットするのを防ぐために、.gitignoreファイルを使ってGit管理から除外することができます。
手動で作成するのは骨が折れるので、.gitignoreのテンプレートを生成してくれるgiboを使うと便利です。
自分は以下のようにテンプレートを生成しています。
gibo dump Python Linux macOS Windows >> .gitignore
echo ".import_linter_cache/" >> .gitignore
VSCode設定
最後にサボりがちなVSCodeのデバッガーの設定についても、簡単にご紹介します。
自分の場合、.vscode/launch.jsonは以下のように設定しています。
{
"version": "0.2.0",
"configurations": [
{
"name": "uv pytest",
"type": "debugpy",
"request": "launch",
"python": "${workspaceFolder}/.venv/bin/python",
"module": "pytest",
"args": [
"--cov",
"--cov-branch",
"-v",
"-s",
],
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "uv debugger",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"python": "${workspaceFolder}/.venv/bin/python",
"console": "integratedTerminal",
"justMyCode": true
}
]
}
Pythonのインタープリターパス${workspaceFolder}/.venv/bin/pythonを指定することで、uvで作成した仮想環境を経由してpytestとpythonファイルを実行できるようになります。
最後に
この記事では自分が使用しているモダンなPython開発環境の概要と、関連するTipsを紹介しました。
今回紹介しきれなかったものに、pytest・tox・hydra・wandbなどありますが、もし興味があれば確認してみてください!
参考記事
Discussion