Ruff で Python の Lint + Format を一括実行する

概要

Python の Lint と Format は、長らく flake8 / isort / black などを組み合わせるのが定番だった。ツールごとに設定ファイルが分かれ、実行も別々で、CI のステップも増える。

Ruff は Rust 製の Lint + Formatter で、これらを 1 つのツールに集約できる。特徴は圧倒的な速さと、既存の主要ルールをほぼカバーしている点。この記事では、Ruff を導入して Lint と Format を一括で回すところまでをハンズオンで進める。

環境

  • Python 3.11
  • Ruff 0.6 系(ruff --version で確認)
  • パッケージ管理は pip または uv を想定

Ruff のバージョンはルールの挙動やデフォルトが変わることがあるため、記事の内容は上記前提とする。手元のバージョンは必ず確認してほしい。

インストール

# pip
pip install ruff

# uv を使う場合
uv add --dev ruff

# バージョン確認
ruff --version

Ruff は単一バイナリで、追加のプラグインを入れなくても多くのルールが最初から使える。

Lint を実行する

# カレント以下をチェック
ruff check .

# 自動修正できるものは修正する
ruff check . --fix

# 修正内容を確認だけしたい(差分表示)
ruff check . --diff

--fix は import の並び替えや未使用 import の削除など、機械的に安全な修正を自動で当てる。安全でない修正まで含めたい場合は --unsafe-fixes を明示的に付ける(挙動が変わる可能性があるため、差分を必ず確認する)。

ruff check . --fix --unsafe-fixes

Format を実行する

Ruff の Formatter は black 互換を意識した整形を行う。

# 整形を適用する
ruff format .

# 整形が必要かどうかだけチェック(CI 向け・書き換えしない)
ruff format . --check

# 差分を表示する
ruff format . --diff

--check は整形が必要なファイルがあると終了コードが非ゼロになるので、CI で「整形されていないコードを弾く」用途に使える。

設定ファイル(pyproject.toml)

設定は pyproject.toml[tool.ruff] セクションとして書くのが基本。プロジェクトルートに置く。

[tool.ruff]
target-version = "py311"
line-length = 100
exclude = [
    ".venv",
    "migrations",
    "__pycache__",
]

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "F",   # pyflakes
    "I",   # isort(import 並び替え)
    "UP",  # pyupgrade
    "B",   # flake8-bugbear
]
ignore = [
    "E501",  # line-too-long(formatter に任せる場合)
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["F401"]

[tool.ruff.format]
quote-style = "double"

select でルール群を「プレフィックス」で指定するのがポイント。E(pycodestyle)や F(pyflakes)は定番、I を入れると isort 相当の import 整列が Ruff だけで完結する。使えるルール一覧は ruff linter で確認できる。

# 有効化できるルール群の一覧
ruff linter

# 特定ルールの説明を見る
ruff rule F401

Lint と Format をまとめて回す

普段の開発では、次の 2 コマンドを続けて実行するのが基本形。

ruff check . --fix
ruff format .

順番は「先に Lint 修正 → 後で Format」が無難。Lint の --fix でコード構造が変わった後に整形をかけると、最終的なレイアウトが安定する。

Makefile にまとめておくと運用が楽になる。

.PHONY: lint check

lint:
	ruff check . --fix
	ruff format .

check:
	ruff check .
	ruff format . --check

pre-commit との連携

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.9  # 実際の最新タグに合わせる
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format
pip install pre-commit
pre-commit install
pre-commit run --all-files

これでコミットのたびに Lint + Format が自動で当たる。

CI での確認方法

CI では「修正」ではなく「検査」を行い、整形・Lint 違反があれば失敗させる。GitHub Actions の例。

name: lint

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: pip install ruff
      - run: ruff check .
      - run: ruff format . --check

ruff check .--fix なし)と ruff format . --check は、いずれも違反があると終了コードが非ゼロになるため、そのまま CI の合否に使える。

確認方法

ruff --version

echo "import os" > /tmp/sample.py
ruff check /tmp/sample.py
# -> F401 [*] `os` imported but unused が出れば OK

注意点

  • バージョン差でルールやデフォルトが変わる。特に Formatter は 0.x 系でも挙動が更新されることがあるため、ruff --version を CI・pre-commit・ローカルで揃える。
  • select を明示しないとデフォルトルール(E + F 相当)のみ。isort 相当(I)や pyupgrade(UP)を効かせたいなら明示的に追加する。
  • 既存プロジェクトへの導入は段階的に。いきなり全ルールを有効化すると差分が膨大になる。まず ruff format で整形を通し、Lint は select を少しずつ増やすのが現実的。
  • black / isort / flake8 から移行する場合、--fixformat で大量の差分が出るので、専用のコミットに分けてレビューしやすくする。

まとめ

Ruff を導入すると、flake8 + isort + black の役割を 1 つのツールに集約でき、設定は pyproject.toml に一本化できる。日常は ruff check . --fix && ruff format .、CI は ruff check .ruff format . --check の組み合わせが基本形になる。速度も速く、既存ツールからの移行コストも小さいので、新規・既存問わず試す価値がある。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください