プロジェクトを共同開発していると、整形やLintの指摘がレビューのノイズになりがちです。
pre-commitを使えば、コミット前に自動でblack/isort/flake8を実行し、コードを整形・静的解析してから履歴に載せられます。
この記事ではPython初心者の方でも安心して導入できるよう、基本から設定例、運用のコツまで丁寧に解説します。
pre-commitとは?
コミット前に自動整形とLintを実行する仕組み
pre-commitはGitのフック(コミット前に走る仕組み)を簡単に管理するためのツールです。
リポジトリのルートに.pre-commit-config.yaml
を置くと、その設定に従ってステージングされたファイルだけに各種チェックが実行されます。
開発者がコミット操作を行うたびに、blackやisort、flake8が自動で動きます。
導入メリット
pre-commitを導入すると、以下のような効果が得られます。
文章で整理すると、まずコードの整形や簡単なミスがコミット前に自動で解消されます。
それによりプルリクエストでの指摘は本質的なロジックに集中しやすくなります。
また、メンバーごとのエディター設定差があっても、リポジトリの設定で統一できるため、環境差異によるスタイルぶれを抑えられます。
最後に、CI前の早期フィードバックにより、不必要なビルドやレビューの手戻りを減らす効果もあります。
使うツール
この記事では次のツールを連携して使います。
pre-commitはフックの実行基盤、blackはコード整形、isortはインポート順の整理、flake8は静的解析(PEP8準拠のスタイルチェックや簡易バグ検出)を担います。
ツール | 役割 | 主な設定の置き場所 |
---|---|---|
pre-commit | Gitフックの実行基盤 | .pre-commit-config.yaml |
black | 自動整形(フォーマッター) | pyproject.toml(推奨) |
isort | インポート順の整列 | pyproject.toml(推奨) |
flake8 | Lint(静的解析) | .flake8 または setup.cfg |
blackとisortは同時運用時にプロファイルを合わせるのがコツです。
isortに--profile=black
を指定し、整形規則を統一します。
導入手順
前提
PythonとGitがインストール済みであることを前提とします。
プロジェクトごとに環境を分けるため、仮想環境(venv)の利用をおすすめします。
以下は代表的な仮想環境の作成と有効化の例です。
# macOS/Linux の例
python3 -m venv .venv
source .venv/bin/activate
# Windows PowerShell の例
py -m venv .venv
.\.venv\Scripts\Activate.ps1
仮想環境が有効化されたら、以降のpipインストールはこのプロジェクト専用に隔離されます。
pre-commitのインストール
整形・Lintのツール一式を開発用依存としてインストールします。
# 開発用ツールのインストール
pip install -U pre-commit black isort flake8
プロジェクトで依存を記録しておくとチームで揃えやすいです。
例えばrequirements-dev.txt
に保存します。
# 依存を記録して共有
pip freeze | grep -E "pre-commit|black|isort|flake8" > requirements-dev.txt
リポジトリに設定ファイルを追加
pre-commit本体の設定は.pre-commit-config.yaml
に記述します。
あわせて、blackやisortの規則をpyproject.toml
に、flake8の規則を.flake8
に記載します。
# .pre-commit-config.yaml
# pre-commitが実行する各ツール(フック)の一覧とバージョンを定義します。
# revは固定(ピン止め)するのが基本です。定期的に `pre-commit autoupdate` で更新しましょう。
minimum_pre_commit_version: "3.5.0"
default_language_version:
python: python3
repos:
# 便利な基本フック集(末尾改行/改行コード/余分な空白など)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
args: ["--fix=lf"] # 改行コードをLFに揃える
# black: コード整形
- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black
language_version: python3
# line-lengthなどはpyproject.tomlに寄せると一元管理できます
# isort: インポート順の自動整理
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
language_version: python3
args: ["--profile=black"] # blackと整形規則を統一
# flake8: Lint
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
language_version: python3
# フィルタや長さ制限などは .flake8 へ
# pyproject.toml
# black と isort の規則をここに集約します。エディターやCIとも共有しやすいです。
[tool.black]
line-length = 88
target-version = ["py310", "py311"]
[tool.isort]
profile = "black"
src_paths = ["src", "tests"]
# .flake8
# flake8の推奨設定例。E203/W503はblackと相性の都合で無視するのが一般的です。
[flake8]
max-line-length = 88
extend-ignore = E203, W503
per-file-ignores =
__init__.py:F401
バージョンは常に固定し、定期的にアップデートするのが安全です。
pre-commit autoupdate
でrev
が自動的に新しい安定版へ更新されます。
設定例
blackのフック設定
blackは「自動で綺麗にする」フォーマッターです。
pre-commitではステージ済みファイルに対して整形を行い、変更があればファイルを上書きします。
上書きが発生した場合は一度コミットが止まり、整形結果を再ステージして再コミットします。
line-lengthや対象のPythonバージョンはpyproject.toml
で統一します。
# .pre-commit-config.yaml (black 部分のみ抜粋)
- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black
language_version: python3
isortのフック設定
isortはインポートの並び替えを自動化します。
blackと組み合わせる場合、--profile=black
を指定して整形方針を合わせます。
細かな除外や対象ディレクトリはpyproject.toml
のtool.isort
に記述します。
# .pre-commit-config.yaml (isort 部分のみ抜粋)
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
language_version: python3
args: ["--profile=black"]
flake8のフック設定
flake8はスタイル違反や未使用変数などを検出します。
blackでは直らない未使用インポート(F401)などはflake8で検出されます。
規則は.flake8
にまとめておくのがわかりやすいです。
# .pre-commit-config.yaml (flake8 部分のみ抜粋)
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
language_version: python3
フックを有効化
設定ファイルを追加したら、リポジトリでpre-commitを有効化します。
これにより.git/hooks/pre-commit
に実行スクリプトが作られます。
pre-commit install
全ファイルで一度実行
既存コードにも規則を一括で適用しておきましょう。
初回は整形や並び替えが多く入るため、専用のコミットにまとめると履歴が読みやすいです。
pre-commit run --all-files
コミット時の動きと失敗時の直し方
実際の挙動を、わざと問題のあるPythonファイルで体験してみます。
# sample.py (わざと悪い例)
# - インポートの順や未使用インポート
# - スペース・セミコロンなどの整形問題
import os, sys # sys は未使用
from typing import List, Dict # 未使用
import json
def add(a:int,b:int)->int:
return a+b
colors=['red','green','blue', 'yellow']
def greet(name:str)->None:
print(f"hello,{name}")
if __name__=="__main__":
greet("world"); print(add(1,2)) # 1行に2文
上記をステージしてコミットすると、pre-commitが動きます。
git add sample.py
git commit -m "Add sample (intentionally bad)"
想定される出力(要約)は次のとおりです。
isort.............................................Reformatted
black.............................................Reformatted
flake8............................................Failed
- hook id: flake8
- exit code: 1
sample.py:1:12: F401 'sys' imported but unused
sample.py:2:1: F401 'typing.List' imported but unused
sample.py:2:1: F401 'typing.Dict' imported but unused
この場合、blackとisortがファイルを書き換えたためコミットは一旦中断され、さらにflake8で未使用インポートのエラーが出ています。
直し方は次のとおりです。
まず未使用インポートを削除し、変更をステージして再コミットします。
# sample.py (修正後の例)
import json
import os # 使っている想定なら残す。未使用ならこれも削除。
def add(a: int, b: int) -> int:
return a + b
colors = ["red", "green", "blue", "yellow"]
def greet(name: str) -> None:
print(f"hello,{name}")
if __name__ == "__main__":
greet("world")
print(add(1, 2))
git add sample.py
git commit -m "Fix: remove unused imports and apply formatting"
成功すると、以後このファイルは整った状態で履歴に積み上がります。
コミット前の自動整形・Lintにより、レビュー前に品質が底上げされます。
運用のコツとトラブル対応
除外設定(files/exclude)の使い方
生成物や移行スクリプトなど、整形・Lintの対象外にしたいパスがある場合は、.pre-commit-config.yaml
で除外します。
各フックごとにfiles
やexclude
を使い分けます。
# 例: data/ と migrations/ をflake8から除外
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
- id: flake8
language_version: python3
exclude: ^(data/|migrations/)
# 例: Pythonファイルのみにblackを適用
- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
- id: black
files: \.pyi?$
正規表現は^
がパス先頭、$
が末尾を意味します。
対象を絞るほど処理が速くなりますが、除外し過ぎると品質が落ちるため、用途を明確にして設定してください。
行末改行や改行コードの違いの対処
OS差やエディター差で起きやすい改行コードの不一致は、pre-commitの基本フックで抑えられます。
上で設定したend-of-file-fixer
とmixed-line-ending
が有効です。
加えて、Gitとエディター側も整えておくと安定します。
Gitの推奨設定例
# macOS/Linux: 改行はLFに統一
git config --global core.autocrlf input
# Windows: チェックアウト時にCRLF、コミット時にLFへ変換
git config --global core.autocrlf true
.gitattributesでの明示的制御例
# テキストはLFへ正規化
* text=auto eol=lf
# バイナリ/画像は改行変換しない
*.png -text
*.jpg -text
VSCodeとの併用
エディターでも同じ規則を使うと、保存時に即座に整形されて快適です。
VSCodeではPython拡張に加えて、black/isortの拡張を導入し、設定で整形を有効化します。
// .vscode/settings.json の例
{
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
},
// 保存時にインポート並べ替え(拡張がisortを呼びます)
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
flake8については専用拡張(例: ms-python.flake8)を入れると、エディター上でLintの警告が見えます。
エディターで整え、pre-commitで最終チェックという二段構えが実用的です。
実行が遅い時のヒント
実行が遅いと感じたら、次の観点で改善できます。
文章で順に説明します。
まず対象ファイルを絞ることが最も効果的です。
files
/exclude
で不要なディレクトリ(例: data/
, dist/
, .venv/
)を外します。
次に、バージョンの固定と定期的な更新を行うと依存の解決が安定し、キャッシュが効きやすくなります。
pre-commitは内部的に仮想環境をキャッシュしますが、破損した場合はpre-commit clean
で一度消すと治ることがあります。
また、全ファイル実行時は並列実行を増やすと効果があります。
# 全ファイル実行を並列化(-jで並列数を指定)
pre-commit run --all-files -j 4
CIでのキャッシュ利用やpre-commit.ci
の活用も有効です。
日常のコミットではステージ済みファイルのみが対象のため、通常は大きな遅延にはなりません。
まとめ
pre-commitを導入すると、コミット前にblack/isort/flake8を自動実行してコード品質を底上げできます。
設定は.pre-commit-config.yaml
に集約し、規則はpyproject.toml
や.flake8
にまとめると、エディターやCIとも整合が取りやすくなります。
典型的なワークフローは、保存時にVSCodeで整形し、コミット時にpre-commitで最終チェックという流れです。
除外設定や改行コードの統一を適切に行えば、レビューのノイズが減って本質的な議論に集中できます。
最初の一歩として、この記事の設定例をそのまま試し、pre-commit install
とpre-commit run --all-files
を実行してみてください。
開発体験が着実に向上するはずです。