コード整形、手動でやってない? - ruff + pre-commitで忘れない仕組みを作る
python 20 min read

コード整形、手動でやってない? - ruff + pre-commitで忘れない仕組みを作る

avatar-m-1

karrinn

著者

KEY TAKEAWAYS

この記事でわかること

  • フォーマッター・リンターの基本(初心者向け)
  • ruffによる3ツール統合(Black + flake8 + isort)
  • pre-commitによる自動実行の仕組み(実行忘れゼロ)

あなたはこんなことで困っていませんか?

多くのPython開発者が、コード品質で以下のような悩みを抱えています。

フォーマッターって何?

  • 「Blackって名前は聞いたことあるけど...」
  • 「インデント、改行、スペース...手動で整えるのめんどくさい」
  • 「チームメンバーとコードスタイルがバラバラ」

リンター厳しすぎ問題

  • 「flake8入れたら怒られまくって開発進まない」
  • 「`E501 line too long`とか、どうでもよくない?」
  • 「設定ファイルの書き方が分からない」

実行忘れる問題

  • 「Black入れたけど、実行するの忘れる」
  • 「フォーマット忘れてPR出したら怒られた」
  • 「毎回 `black .` 打つのめんどくさい」

この記事で解決できること

  • フォーマッター・リンターの基本を理解(初心者でもOK)
  • 現実的なリンター設定(厳しすぎない、段階的に導入)
  • 自動実行で忘れない仕組み(pre-commitで完全自動化)

そもそもフォーマッター・リンターって何?

まずは基本から。「フォーマッター」「リンター」「pre-commit」の3つの用語を理解しましょう。

TERM 01

フォーマッター(Formatter)

役割: コードのスタイルを自動整形

  • インデント(スペース2個 or 4個)
  • 改行位置
  • 引用符(シングル or ダブル)
  • 行の長さ

TERM 02

リンター(Linter)

役割: コードの問題を検出

  • 未使用変数
  • import順序
  • 潜在的なバグ
  • 命名規則違反

TERM 03

pre-commit

役割: git commit前に自動実行

  • フォーマッター・リンターを自動実行
  • コミット前に問題を修正
  • 「実行忘れ」がなくなる

TERM 04

ruff

役割: フォーマット + リント + import整理を統合

  • Black + flake8 + isortを1つに統合
  • Rustベースで爆速(10-100倍高速)
  • 設定ファイル1個(pyproject.toml)

フォーマッターの効果:ビフォー・アフター

フォーマッターを使うと、読みづらいコードが一瞬で整います。

Python: フォーマット前(読みづらい)
def calculate(x,y,z):
result=x+y+z
return result

def process_data(data,options = {'key'  :'value'}):
if data>0:
return  data*2
return None
Python: フォーマット後(読みやすい)
def calculate(x, y, z):
result = x + y + z
return result


def process_data(data, options={"key": "value"}):
if data > 0:
return data * 2
return None

なぜフォーマッターが必要?

  • チーム全員のコードスタイルが統一される:誰が書いても同じスタイル
  • レビュー負担軽減:「インデント直して」みたいな指摘がなくなる
  • 可読性向上:読みやすいコードは保守しやすい

リンターの効果:問題の検出

リンターは、潜在的なバグやコーディング規約違反を自動検出します。

Python: リンターが検出する問題例
# リンターが検出する問題例
import os
import sys
import django  # ❌ E402: import順序が間違ってる(標準→サードパーティ)

def process_data(data):
result = data * 2
temp = 100  # ❌ F841: 未使用変数
return result

def calculate(x):
if x=10:  # ❌ E999: 構文エラー(== が正しい)
return x * 2

なぜリンターが必要?

  • バグの原因を事前に検出:実行前に問題を発見
  • コードの品質向上:ベストプラクティスに準拠
  • レビュー負担軽減:機械的なチェックはツールに任せる

従来の辛さ:Black + flake8 + isort

従来は、フォーマット・リント・import整理を別々のツールで実行していました。これが面倒で、多くの問題を引き起こしていました。

Bash: 従来の辛い運用
# 1. 複数ツールのインストール
pip install black flake8 isort

# 2. 設定ファイルが分散
# .flake8, pyproject.toml, setup.cfg...どこに何を書く?

# 3. 実行も別々
black .
isort .
flake8 .
# → 3コマンド打つのめんどくさい

# 4. 設定が競合
# Blackとflake8の行長設定が違ってエラー...
比較項目Black + flake8 + isortruff
ツール数3個1個
インストール3回1回
設定ファイル複数(.flake8, pyproject.toml等)1個(pyproject.toml)
実行コマンド3個1個
速度(10万行プロジェクト)Black: 5.2秒 + flake8: 20秒0.5秒(50倍速い)
学習コスト高い(3つの設定を学ぶ)低い(1つだけ)

"ツールは少なく、効果は最大に。
開発者の時間は、設定ファイルではなく、価値の創造に使うべき。"

解決策:ruffとは何か

ruffは、Black、flake8、isort、pydocstyle等を1つに統合したオールインワンツールです。Rustで実装されており、従来ツールの10-100倍高速です。

ruff

オールインワン

Rustベースの超高速Python linter & formatter。Black、flake8、isort等を統合。

公式サイト

GitHub

pre-commit

自動化

git commit前に自動でフォーマット・リントを実行。実行忘れをゼロに。

公式サイト

ruffのメリット (Pros)

  • オールインワン: Black, flake8, isort, pydocstyle等を統合
  • 爆速: 10-100倍高速(Rustベース)
  • シンプル: 設定ファイル1個(pyproject.toml)
  • VSCode対応: 公式拡張機能あり

ruffのデメリット (Cons)

  • 一部ルール未実装: flake8の全プラグインには未対応(99%は対応済み)
  • 歴史が浅い: 2022年リリース(Black/flake8ほど枯れていない)

ruff導入手順:10分でできます

ruffの導入は非常に簡単です。以下の4ステップで完了します。

  • 01

    インストール(1分)

    Bash
    # uvを使う場合(推奨)
    uv pip install ruff
    
    # pipを使う場合
    pip install ruff
    
    # 確認
    ruff --version

    uvについては、uv記事を参照してください。

  • 02

    初回実行(1分)

    Bash
    # フォーマット実行
    ruff format .
    
    # リント実行
    ruff check .
    
    # リント+自動修正
    ruff check --fix .

    まずは設定なしで実行してみましょう。デフォルト設定でも十分使えます。

  • 03

    設定ファイル作成(3分)

    設定のポイント

    プロジェクトルートに pyproject.toml を作成します。最初は緩めの設定で始めて、慣れたら厳しくするのがおすすめです。

    • line-length: 行の長さ(100文字推奨)
    • select: 有効にするルール
    • ignore: 無視するルール(E501は無視が現実的)

    ポイント: E501(行長すぎ)は無視するのが現実的。

    TOML: pyproject.toml
    [tool.ruff]
    # 行の長さ(デフォルト88、Blackと同じ)
    line-length = 100
    
    # Python バージョン
    target-version = "py311"
    
    [tool.ruff.lint]
    # 有効にするルール
    select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes
    "I",   # isort(import整理)
    "N",   # pep8-naming
    ]
    
    # 無視するルール(現実的な設定)
    ignore = [
    "E501",  # line too long(100文字までOKにしてるので)
    ]
    
    [tool.ruff.lint.isort]
    # Django用のimport順序
    known-first-party = ["myproject"]
  • 04

    VSCode設定(3分)

    自動フォーマットの設定

    .vscode/settings.json を作成して、保存時に自動フォーマットを有効化します。

    • defaultFormatter: ruffを設定
    • formatOnSave: 保存時に自動実行
    • codeActionsOnSave: リント&import整理も自動化

    保存時に自動フォーマット!

    JSON: .vscode/settings.json
    {
    "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.organizeImports": true
    }
    }
    }

悩み別の解決策

悩み1解決: フォーマッターの使い方

Bash: ruff formatコマンド
# ファイル全体をフォーマット
ruff format .

# 特定ファイルのみ
ruff format myfile.py

# 確認のみ(変更しない)
ruff format --check .

実例: フォーマット前後の変化

Python: Before
def my_function(arg1,arg2,arg3):
if arg1>0:
return arg1+arg2+arg3
return 0
Python: After(ruff format実行後)
def my_function(arg1, arg2, arg3):
if arg1 > 0:
return arg1 + arg2 + arg3
return 0

悩み2解決: リンター設定(プロジェクトにあった設定例)

リンターを後から導入する場合は、いきなり厳しくしすぎると挫折します。最低限でもかなりの効果があるのでプロジェクトにあった厳しさにしましょう。もちろん後から徐々に厳しくしていくこともできます。

厳しさの戦略パターン

段階的導入のポイント

最初から全ルールを有効にすると、エラーの嵐で挫折します。

  • パターン1: 基本的なエラーのみ(E, F)
  • パターン2: import整理追加(I)
  • パターン3: 命名規則追加(N)
TOML: 段階的導入
# パターン1: 最低限
select = ["E", "F"]

# パターン2: import整理追加
select = ["E", "F", "I"]

# パターン3: 命名規則追加
select = ["E", "F", "I", "N"]

悩み3解決: pre-commitで自動化

pre-commitを導入すると、git commit時に自動でruffが実行されます。実行忘れがゼロになります。

Step 1: pre-commitインストール

Bash
uv pip install pre-commit
# or
pip install pre-commit

Step 2: 設定ファイル作成

pre-commit設定のポイント

プロジェクトルートに .pre-commit-config.yaml を作成します。2つのフックを登録します。

  • ruff-check: リント実行(自動修正あり)
  • ruff-format: フォーマット実行
  • language: system: ローカルにインストールしたruffを使用
YAML: .pre-commit-config.yaml
repos:
- repo: local
hooks:
# ruff check(リント)
- id: ruff-check
name: ruff check
entry: ruff check --fix
language: system
types: [python]
pass_filenames: false

# ruff format(整形)
- id: ruff-format
name: ruff format
entry: ruff format
language: system
types: [python]
pass_filenames: false

Step 3: pre-commit有効化

Bash
pre-commit install

これで完了!以降はgit commitすると自動実行

Bash: 自動実行フロー
# コミット前に自動でruffが実行される
git add .
git commit -m "Add feature"
# → ruffが自動実行 → 問題あれば修正 → コミット

開発フロー(pre-commit導入後)

pre-commit導入後の開発フローを図解します。

  • 1

    コード編集

    通常通りPythonコードを編集

  • 2

    ファイル保存

    VSCode設定で自動フォーマット

  • 3

    ruff formatが自動実行

    保存時に自動フォーマット(VSCode設定による)

  • 4

    git add .

    変更をステージング

  • 5

    git commit

    コミット実行(pre-commitが発動)

  • 6

    ruff check --fixが自動実行

    pre-commitフックによる自動リント

  • 7

    自動修正 or エラー表示

    問題があれば修正、または手動修正が必要な場合はエラー表示

  • 8

    コミット完了

    「実行忘れ」が発生しない!

article-sample-srcでの実際の使用例

サンプルリポジトリ(article-sample-src)での実例を紹介します。そのままコピーして使えます。

ディレクトリ構造

Tree
article-sample-src/
├── backend/
│   ├── pyproject.toml          # ruff設定
│   ├── .pre-commit-config.yaml # pre-commit設定
│   └── myproject/
└── .vscode/
└── settings.json           # VSCode設定

実際のpyproject.toml(全文)

実プロジェクトでの設定

サンプルリポジトリで実際に使っている設定です。そのままコピーして使えます。

  • exclude: Djangoマイグレーションファイルなどを除外
  • select: E, W, F, I, Nの5つのルールセット
  • isort: プロジェクト固有のimport整理設定
TOML: backend/pyproject.toml
[tool.ruff]
line-length = 100
target-version = "py311"

# 除外ディレクトリ
exclude = [
"migrations/",
"*.pyi",
"__pycache__",
]

[tool.ruff.lint]
select = [
"E",   # pycodestyle errors
"W",   # pycodestyle warnings
"F",   # pyflakes
"I",   # isort
"N",   # pep8-naming
]

ignore = [
"E501",  # line too long
]

[tool.ruff.lint.isort]
known-first-party = ["myproject"]

実際の.pre-commit-config.yaml(全文)

pre-commit実設定

サンプルリポジトリで使っている設定です。コミット前に自動でruffが実行されます。

  • repo: local: ローカルにインストールしたruffを使用
  • 2つのフック: check(リント)とformat(整形)
  • pass_filenames: false: プロジェクト全体を対象
YAML: backend/.pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: ruff-check
name: ruff check
entry: ruff check --fix
language: system
types: [python]
pass_filenames: false

- id: ruff-format
name: ruff format
entry: ruff format
language: system
types: [python]
pass_filenames: false

サンプルリポジトリ

ruff + pre-commitの実際の設定を公開

GitHubで見る

パフォーマンス比較:ruffの速さ

ruffは、従来ツール(Black、flake8等)と比較して圧倒的に高速です。以下は、10万行のDjangoプロジェクトでの実測データです。

リント速度比較(10万行プロジェクト)

フォーマット速度比較(10万行プロジェクト)

Speed Improvement

10-100x

従来ツール比

Technology

Rust

実装言語

Column: Pythonフォーマッター戦争の歴史

  • 2001年: PEP 8(Pythonコーディング規約)策定 - PEP 8
  • 2010年: pep8(後のpycodestyle)登場
  • 2013年: autopep8登場(自動整形)
  • 2015年: yapf登場(Google製)
  • 2018年: Black登場(opinionated formatter) - Black公式
  • 2019年: Black大流行(設定不要の思想が受け入れられる)
  • 2022年: ruff登場(Rustベース、オールインワン) - Astral社
  • 2024年: ruff採用率急上昇(Stack Overflow Survey 2024: Python開発者の84%がリンター使用)

なぜruffが選ばれる? Black(フォーマット)+ flake8(リント)+ isort(import)を1つに統合し、Rustベースで爆速、設定もシンプル。開発者の時間を設定ではなく価値創造に使える。

よくあるトラブル

Pro Tip 1: pre-commitが動かない

Bash
# pre-commitのインストール確認
pre-commit --version

# hookの再インストール
pre-commit uninstall
pre-commit install

Pro Tip 2: 特定ファイルを除外

除外設定のポイント

自動生成ファイルや型スタブなど、チェック不要なファイルを除外します。

  • migrations/: Djangoマイグレーション
  • *.pyi: 型スタブファイル
  • __pycache__: キャッシュディレクトリ
TOML: pyproject.toml
[tool.ruff]
exclude = [
"migrations/",     # Djangoマイグレーション
"*.pyi",          # 型スタブ
"__pycache__",
]

Pro Tip 3: pre-commitスキップ(緊急時のみ)

本当に緊急の場合のみ使用してください。

Bash
# たまにはスキップしたい時
git commit --no-verify -m "urgent fix"

Black/flake8からの移行ガイド

既存プロジェクトでBlackやflake8を使っている場合の移行手順です。

パターンA: Blackのみ使用中

Bash
# 1. ruffインストール
uv pip install ruff

# 2. Blackアンインストール
pip uninstall black

# 3. ruff実行
ruff format .

Blackの設定は、ruffのデフォルトとほぼ互換性があります。

パターンB: Black + flake8使用中

Bash
# 1. ruffインストール
uv pip install ruff

# 2. 既存設定を移行
# .flake8の設定をpyproject.tomlに移す

# 3. 動作確認
ruff check .
ruff format .

# 4. 問題なければアンインストール
pip uninstall black flake8 isort

flake8の設定は、ruffのルール番号にほぼ対応しています。

CI/CDでの使用例

GitHub Actionsでruffを実行する例です。pre-commitだけでなく、CI/CDでもチェックすると安心です。

GitHub Actionsの設定

CI/CDでもruffチェックを実行して、コード品質を保証します。pre-commitをスキップされても安心。

  • checkout: コードを取得
  • uv install: 高速なパッケージマネージャー
  • ruff check: リント実行
  • ruff format --check: フォーマット確認(変更しない)
YAML: .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Setup Python
run: |
    uv venv
    source .venv/bin/activate
    uv pip install ruff
- name: Run ruff
run: |
    ruff check .
    ruff format --check .

参考: GitHub Actions公式ドキュメント

よくある質問 (FAQ)

A. ほぼ可能です。 ruffのフォーマッターはBlackと互換性があります。Blackのpyproject.toml設定(line-lengthtarget-version等)は、ruffでもそのまま使えます。ただし、100%完全互換ではないため、移行時は差分を確認することをおすすめします。

A. 一部は使えません。 ruffはflake8プラグインを直接サポートしていませんが、主要なルール(flake8-bugbearflake8-comprehensions等)は既にruffに組み込まれています。詳細はruff公式ルール一覧を参照してください。

A. ruff爆速なので、体感できるほど遅くなりません。 従来のBlack + flake8の組み合わせだと数秒かかっていたチェックが、ruffなら1秒未満で完了します。pre-commit導入による遅延は、ほぼ気になりません。

A. 推奨します。ただしCI/CDでもチェックすると安心。 pre-commitは--no-verifyでスキップできるため、CI/CDでもruffチェックを実行することで、確実にコード品質を保証できます。チーム全員に導入を推奨し、CI/CDをセーフティネットとして使うのがベストプラクティスです。

次のステップ

コード品質・自動化の悩みは解決しました。次は、型チェックや依存関係管理に進みましょう。

型チェックで開発が遅くなる?

【Python × Pyright】型チェックって何? - Pyrightの現実的な設定で、厳しすぎない型チェックを実現する

記事を読む

requirements.txtはもう限界じゃない?

Python環境管理はuvが最高 - おすすめのPython環境管理

記事を読む

Pythonプロジェクトで困ってるのは基本の環境を用意してないからじゃない?

Pythonプロジェクトの長期運用で本当に必要なこと - 環境・品質・型の3本柱

記事を読む

"自動化できることは、自動化すべきだ。"

開発者の時間は、価値の創造に使うために存在する

参考文献・リンク

関連トピック

コメント (0)

まだコメントはありません。最初のコメントを残しませんか?

コメントを投稿

メールアドレスが公開されることはありません。必須項目には * が付いています