第7章 実践:uv・Ruff・型で作るモダンプロジェクト
これまでに学んだ「uv によるプロジェクト管理」「Ruff によるコード品質管理」「Python の静的型付け」「dataclass と Pydantic によるデータモデリング」を統合し、実際に動くモダンな Python プロジェクトを構築・運用する一連の流れを体験します。
本章では、シンプルなタスク管理アプリケーションを題材に、初期化から実装、検証、実行までの標準的な開発サイクルをハンズオン形式で進めます。
7.1 プロジェクトの初期化と構成
まず、新しい作業ディレクトリを作成し、uv init でプロジェクトを初期化します。
uv init task-manager
cd task-manager
ディレクトリ構成の設計
保守性の高い標準的な Python プロジェクト構成を作成します:
task-manager/
├── pyproject.toml # プロジェクト定義と各種ツール設定
├── .python-version # Python バージョン
├── README.md # プロジェクト概要
└── src/
└── task_manager/
├── __init__.py # パッケージ初期化ファイル
├── models.py # 型定義とデータモデル
├── service.py # ビジネスロジック
└── main.py # エントリーポイント
7.2 開発ツールの追加
開発に必要なリンター・フォーマッター(ruff)および静的型チェッカー(ty)を開発用依存関係として追加します。
uv add --dev ruff ty
7.3 pyproject.toml の設定
プロジェクトルートの pyproject.toml に、Ruff と ty の動作設定をまとめて記述します。
[project]
name = "task-manager"
version = "0.1.0"
description = "uv, Ruff, 型ヒントを活用したモダン Python プロジェクトのサンプル"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# --- Ruff 設定 ---
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = [
"E", # pycodestyle エラー
"W", # pycodestyle 警告
"F", # Pyflakes
"I", # isort (インポート整列)
"B", # flake8-bugbear
"UP", # pyupgrade
"RUF", # Ruff 独自ルール
]
ignore = ["E501"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
# --- ty 設定 ---
[tool.ty]
[tool.ty.rules]
unused-ignore-comment = "warn"
redundant-cast = "ignore"
possibly-missing-attribute = "error"
7.4 アプリケーションの実装
型安全性を重視した各モジュールを実装します。
データモデル (src/task_manager/models.py)
第5章および第6章で学んだ Literal、TypedDict、および @dataclass(frozen=True) を活用してデータ構造を定義します。
from dataclasses import dataclass
from typing import Literal, TypedDict
type TaskStatus = Literal["todo", "in_progress", "done"]
class TaskDict(TypedDict):
"""JSON 連携用の辞書型表現"""
id: int
title: str
status: TaskStatus
@dataclass(frozen=True)
class Task:
"""タスクを表す不変データモデル"""
id: int
title: str
status: TaskStatus = "todo"
def to_dict(self) -> TaskDict:
return {
"id": self.id,
"title": self.title,
"status": self.status,
}
サービスロジック (src/task_manager/service.py)
型注釈を漏れなく記述し、タスクの登録・検索・状態変更を行うサービスクラスを実装します。
from task_manager.models import Task, TaskStatus
class TaskService:
"""タスクを管理するサービスクラス"""
def __init__(self) -> None:
self._tasks: dict[int, Task] = {}
self._next_id: int = 1
def add_task(self, title: str) -> Task:
"""新しいタスクを追加する"""
task = Task(id=self._next_id, title=title)
self._tasks[task.id] = task
self._next_id += 1
return task
def get_task(self, task_id: int) -> Task | None:
"""ID を指定してタスクを取得する(存在しない場合は None)"""
return self._tasks.get(task_id)
def update_status(self, task_id: int, new_status: TaskStatus) -> Task:
"""タスクの進行状況を更新する"""
current_task = self.get_task(task_id)
if current_task is None:
raise ValueError(f"Task with ID {task_id} not found")
updated_task = Task(
id=current_task.id,
title=current_task.title,
status=new_status,
)
self._tasks[task_id] = updated_task
return updated_task
def list_tasks(self, status: TaskStatus | None = None) -> list[Task]:
"""条件に一致するタスク一覧を取得する"""
tasks = list(self._tasks.values())
if status is not None:
return [task for task in tasks if task.status == status]
return tasks
エントリーポイント (src/task_manager/main.py)
作成したサービスを呼び出して実行するメイン関数を記述します。
from task_manager.service import TaskService
def main() -> None:
service = TaskService()
# タスクの登録
task1 = service.add_task("uv の使い方を学ぶ")
task2 = service.add_task("Ruff でコードを検証する")
print(f"作成タスク: {task1.title} (状態: {task1.status})")
# タスクの完了更新
service.update_status(task1.id, "done")
# 一覧の表示
print("\n--- 全タスク一覧 ---")
for task in service.list_tasks():
print(f"[{task.status.upper()}] ID={task.id}: {task.title}")
if __name__ == "__main__":
main()
7.5 コード品質の検証とフォーマット
作成したコードに対して、Ruff によるリント検査とコードフォーマットを実行します。
1. リント検査と自動修正
uv run ruff check --fix .
不要なインポートの除去やインポート順序の整列が瞬時に実行されます。
2. コードフォーマット
uv run ruff format .
インデントやクォートが PEP 8 / Black スタイルに自動統一されます。
7.6 静的型チェックの実行
次に、ty を実行して型定義の整合性を検証します。
uv run ty check src
すべての引数・戻り値・型パラメータが正しく整合していれば、以下のように出力されます:
All checks passed!
もし型の不整合や None 参照の危険がある場合は、具体的なファイルと行番号とともに警告が表示され、不具合を本番実行前に修正できます。
7.7 アプリケーションの実行
最後に、uv run を用いてアプリケーションを実行します。
uv run python -m task_manager.main
期待通りの動作がターミナルに表示されれば、完成です:
作成タスク: uv の使い方を学ぶ (状態: todo)
--- 全タスク一覧 ---
[DONE] ID=1: uv の使い方を学ぶ
[TODO] ID=2: Ruff でコードを検証する
7.8 モダン開発サイクルのまとめ
本章で実践したモダン Python 開発の標準的なルーティンは以下の通りです:
- 環境と依存関係の管理:
uv addやuv syncを使い、常に再現性のある仮想環境を保つ。 - 型を意識した設計: 変数・関数・モデルに明確な型注釈を付与し、自己文書化と不具合抑止を図る。
- 高速リントと整形: 保存時またはコミット前に
uv run ruff check --fixとuv run ruff formatを実行する。 - 厳格な型検証:
uv run ty checkで静的解析を行い、型安全性を担保する。 - 安全な実行:
uv runを介して環境差分のない状態で実行・テストする。
これらのツールとプラクティスを組み合わせることで、高速でストレスのない、極めて信頼性の高い Python 開発が実現できます。