Pythonを使った開発をしているとき、コードレビューでコーディングスタイルなどに関する表面的な指摘をするのが面倒だ。効率化するために、コミットの時点でこれらについてのチェックを自動で行い、エラーになった場合はコミットできないようにする。

はじめGit標準のpre-commit hookでやろうと思っていたが、pre-commitの方が設定や管理が楽そうなので、これを選んだ。

やりたいこと

コミット時に以下のチェックを行い、NGならコミットできなくする

  • オートフォーマット
    autopep8を使う。
  • コーディングスタイルのチェック
    flake8を使う。
  • 型ヒントのチェック
    mypyを使う。

pre-commitとは

Git標準のpre-commit hookを管理するためのツール。設定はyaml管理で書ける。
言語としてはPython以外にGolang、Rustなどもサポートしている

方法

  1. pre-commitのインストール

    pip3 install pre-commit
    
  2. 対象のローカルリポジトリのルートにcd
    当然だが、git管理されているリポジトリでないといけない。

  3. .pre-commit-config.yaml の作成・編集
    .pre-commit-config.yaml という名前でファイルを作り、以下のように書く。なお rev はその repo で使いたいリビジョンやタグを書く。

    -   repo: https://github.com/pre-commit/mirrors-autopep8
        rev: v1.5
        hooks:
        -   id: autopep8
    -   repo: https://gitlab.com/pycqa/flake8
        rev: 3.7.9
        hooks:
        -   id: flake8
    -   repo: https://github.com/pre-commit/mirrors-mypy
        rev: v0.761
        hooks:
        -   id: mypy
            args: [--ignore-missing-imports]
    
  4. Gitのpre-commit hookの設定

    pre-commit install
    

    (この時点では .pre-commit-config.yaml に書いた各種ツールはまだダウンロードしていない。単にそのリポジトリの .git 以下にhookスクリプトが入るだけ。ダウンロードするのは次回のコミット時。)

これで設定完了。あとはいつも通り、ファイル修正して git addgit commit すればいい。設定したチェックが走り、エラーがあればコミットができなくなる。

なお以下のコマンドならコミットのタイミング以外でもチェックできる。

pre-commit run --all-files

試す

以下のような問題のあるコード(app.py)をコミットしようとした。

msg:int="Hello, world!"
print( msg )

チェックされて、次のようなエラー。コミットできない!(flake8によるチェックが通っているのはautopep8が修正したため。)

$ git commit
autopep8.................................................................Failed
- hook id: autopep8
- files were modified by this hook
flake8...................................................................Passed
mypy.....................................................................Failed
- hook id: mypy
- exit code: 1
- files were modified by this hook

app.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")
Found 1 error in 1 file (checked 1 source file)

以下のように修正して再コミット。(フォーマットはautopep8がやってくれたので、型ヒントだけ修正すればいい。)

msg: str = "Hello, world!"
print(msg)

これで通る。

$ git commit
autopep8.................................................................Passed
flake8...................................................................Passed
mypy.....................................................................Passed

あとはいつもと同じ。

補足

.pre-commit-config.yaml に書いたautopep8とかはどこに入ったのだろうと探したら ~/.cache/pre-commit 以下に入っていた。これはpre_commit/store.pyで設定しているよう。

参考