📘

pre-commit で Python コードをキレイに管理してみた

2023/01/25に公開

pre-commit とは

pre-commit は、Git の pre-commit フックスクリプトを管理・メンテナンスするためのツールです。簡単に言うと、git コマンドでコミットする前に、文法や体裁をチェックしてくれるものになります。

pre-commit インストール

shuma@fika subprocess % pipenv install --dev pre-commit

設定ファイルの作成

pre-commit の設定ファイルを作成します。pipenv でインストールしたコマンドは pipenv run <command> のように使います。

fika:subprocess shuma$ pipenv run pre-commit sample-config > .pre-commit-config.yaml
fika:subprocess shuma$ ls -a
.			Pipfile.lock		test2.py
..			calc.py			test3.py
.pre-commit-config.yaml	error_test.py
Pipfile			test1.py

設定ファイルが作成されました。中身は以下のようになっています。

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files
  • repo
    hook スクリプトが存在するリポジトリを指定します。 GitHub で公開されているリポジトリを指定すれば OK。
  • rev
    repo で指定されたリポジトリのリビジョンもしくはタグを指定します。
  • hooks
    実行する hook スクリプトをリストで指定します。
  • id
    各 hook スクリプトの ID を指定します。 リポジトリ内に存在する hook スクリプトのファイル名と一致(.pyは不要)するよう設定します。

git を導入(すでに git を使用していれば飛ばして OK)

fika:subprocess shuma$ git init
fika:subprocess shuma$ git add .
fika:subprocess shuma$ git commit -m "init"
[master (root-commit) 9184619] init
 8 files changed, 176 insertions(+)
 create mode 100644 .pre-commit-config.yaml
 create mode 100644 Pipfile
 create mode 100644 Pipfile.lock
 create mode 100644 calc.py
 create mode 100644 error_test.py
 create mode 100644 test1.py
 create mode 100644 test2.py
 create mode 100644 test3.py

設定ファイルから hook をインストール

fika:subprocess shuma$ pipenv run pre-commit install
pre-commit installed at .git/hooks/pre-commit

# 適当にファイルに変更を加えてコミットしてみる。
fika:subprocess shuma$ git add .
fika:subprocess shuma$ git commit -m "add hello"
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped
Check for added large files..............................................Passed
[master 3e9a96f] add hello
 1 file changed, 1 insertion(+)

このように設定ファイルに書いた内容に従って、変更のあったファイルはコミット前にチェックがされます。

hook をカスタマイズ

利用可能な hook は[1]を参考にできる。

flake8, yapf, mypy, pylint を追加してみる。

  • flake8
    Pythonのコードチェッカーツール(ライブラリ)です。ignore でチェックを無視する項目を追加できます。例:定義したけど使っていない変数があると怒ってきます。[2]
  • yapf
    Pythonコードを整形(フォーマット)できる。
  • mypy
    mypy とは、Python のコードを型チェックできるライブラリです。型チェックとは、指定した型通りの値が変数に代入されているか、引数が渡されているか、戻り値になっているか、などを静的に検査することです。
  • pylint
    Python プログラムを解析し、バグの原因となりそうな箇所をチェックします。(flake8 と似たようなやつ)

設定ファイルは以下のようになります。

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
    -   id: trailing-whitespace
    -   id: end-of-file-fixer
    -   id: check-yaml
    -   id: check-added-large-files

-   repo: https://github.com/pycqa/flake8
    rev: 4.0.1
    hooks:
    -   id: flake8
        additional_dependencies:
          - flake8-isort
        args: ["--max-line-length=110", "--ignore=E266,W503"]

-   repo: https://github.com/google/yapf
    rev: v0.32.0
    hooks:
      - id: yapf

- repo: https://github.com/pre-commit/mirrors-mypy
  rev: v0.971
  hooks:
    - id: mypy
      args: [--ignore-missing-imports, --show-column-numbers]

- repo: https://github.com/pycqa/pylint
  rev: v2.14.5
  hooks:
  -   id: pylint
      args: ["--rcfile=pylintrc"]

コミットしようとしたときの結果は以下になります。

fika:subprocess shuma$ git commit -m "add white line"
[ERROR] Your pre-commit configuration is unstaged.
`git add .pre-commit-config.yaml` to fix this.
fika:subprocess shuma$ git add .
fika:subprocess shuma$ git commit -m "add white line"
[INFO] Initializing environment for https://github.com/pycqa/flake8.
[INFO] Initializing environment for https://github.com/pycqa/flake8:flake8-isort.
[INFO] Initializing environment for https://github.com/google/yapf.
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Initializing environment for https://github.com/pycqa/pylint.
[INFO] Installing environment for https://github.com/pycqa/flake8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/google/yapf.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/pylint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...............................................................Passed
Check for added large files..............................................Passed
flake8...................................................................Failed
- hook id: flake8
- exit code: 1

test1.py:2:1: I004 isort found an unexpected blank line in imports
test1.py:3:1: I004 isort found an unexpected blank line in imports
test1.py:5:1: E303 too many blank lines (3)

yapf.....................................................................Failed
- hook id: yapf
- files were modified by this hook
mypy.....................................................................Passed
pylint...................................................................Failed
- hook id: pylint
- exit code: 32

The config file pylintrc doesn't exist!

flake8 では、不要な空行や多すぎる空行を指摘しています。しかし、これは勝手に修正してくれます。

flake8...................................................................Failed
- hook id: flake8
- exit code: 1

test1.py:2:1: I004 isort found an unexpected blank line in imports
test1.py:3:1: I004 isort found an unexpected blank line in imports
test1.py:5:1: E303 too many blank lines (3)

yapf でもエラーは出ていますが、勝手に修正されているようです。

yapf.....................................................................Failed
- hook id: yapf
- files were modified by this hook

mypy では特にエラーは出ていません。

mypy.....................................................................Passed

pylint は、pylint 用の設定ファイルを用意していなかったのでエラーになりました。

pylint...................................................................Failed
- hook id: pylint
- exit code: 32

The config file pylintrc doesn't exist!

pylintrc という名前のファイルを作ります。内容は以下のようにしました。

[MASTER]

# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=cx_Oracle

[BASIC]
argument-rgx=^[a-z][a-z0-9]*((_[a-z0-9]+)*)?$

[MESSAGES CONTROL]
disable=W0621,R0801,R0915,C0302,R0912,R0913,R0914,R0902,C0209,R1734,E2515,C2801,E0401,W0102

pre-commit が全て通らなかったので、今回のコミットはできていません。

もう一度コミットします。

fika:subprocess shuma$ git add .
fika:subprocess shuma$ git commit -m "add pylintrc"
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...............................................................Passed
Check for added large files..............................................Passed
flake8...............................................(no files to check)Skipped
yapf.................................................(no files to check)Skipped
mypy.................................................(no files to check)Skipped
pylint...............................................(no files to check)Skipped
[master 661e974] add pylintrc
 2 files changed, 37 insertions(+)
 create mode 100644 pylintrc

全ての pre-commit が Passed か Skipped されたらコミットされます。

これで、コミットする度にソースコードがキレイな形に整形されるようになります。

脚注
  1. https://pre-commit.com/hooks.html ↩︎

  2. https://flake8.pycqa.org/en/latest/user/using-hooks.html ↩︎

Discussion