🎁

Python project用にlinter / formatterを一通り整備する

2022/02/23に公開

Python で自作モジュールを実装する際に、linter / formatter を一通り整備したので理解した内容を備忘録的にまとめてみます。

flake8 (linter)

flake8 は pycodestyle, pyflakes, mccabe の 3 つの linter をまとめた wrrapper ツールです。

https://github.com/PyCQA/flake8

pycodestyle は、PEP8にまとめられているコーディング規約をベースにしており、コードスタイルについてのチェックを行います。
コードスタイルに違反している箇所がある場合は、行数と下記の Document に定義されたエラーコードが返されます。

https://github.com/PyCQA/pycodestyle/blob/main/docs/intro.rst

設定自体は、pyproject.toml 側に定義しています。
後述する、tox の設定ファイル(tox.ini)にも記述できますが、ツール群の設定をなるべく一箇所に寄せるという意味でも pyproject.toml に記述しています。

flake8 --extend-ignore=W234のようにコマンド実行時に option 指定する設定も pyproject.toml に定義します
オプション自体は下記の Document を参考にします。

https://flake8.pycqa.org/en/latest/user/options.html

pyproject.toml
[flake8]
max-line-length = 120
extend-ignore = W234

コードを書きながら CI で lint を回して、必要に応じて特定のエラー自体を ignore したり、in-line で ignore する形で調整しています。

https://flake8.pycqa.org/en/latest/user/violations.html#in-line-ignoring-errors

black (formatter)

ソースコードを自動で整形する code formatter です。

https://github.com/psf/black

--include, --excludeのような対象・除外ファイルの指定はできますが、black 自体は設定の余地が少なく制限が強いのが特徴です。

https://black.readthedocs.io/en/stable/usage_and_configuration/index.html

pyproject.toml にも対応しているので、black の設定も寄せています。

https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-format

pyproject.toml
[tool.black]
target-version = ['py38', 'py39']

isort

また、上記に加えて、import 文を自動で整理するツールとして isort も利用しています。

https://github.com/PyCQA/isort

これにより PEP8 に準拠した import の順序に修正できます。

https://www.python.org/dev/peps/pep-0008/#imports

オプションは下記の Document にまとめられており、一部ですが pyproject.toml のサンプルもあります。

https://pycqa.github.io/isort/docs/configuration/options.html

注意点として、既に利用している formatter と設定が conflict するケースがあるので、それを避けるために isort 側で formatter(black)を profile として指定します。

https://pycqa.github.io/isort/docs/configuration/profiles.html

pyproject.toml
[tool.isort]
profile = "black"
lines_after_imports = 2

mypy

今回の Python project は、python3.9,10 を前提にしているので、型アノテーションをもとに静的型チェックを行う mypy も利用します。

https://github.com/python/mypy

型アノテーション自体は Python3.5 から導入されたType Hintsを利用しており、mypy の公式 Document にチートシートもあるので、この辺りを参考にアノテーションを付けていきます。

https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html

設定自体は mypy.ini にも定義可能ですが、mypy の設定も pyproject.toml に寄せます。
グローバルな設定を定義する section と module ごとの設定を定義する section に分かれており、module ごとの設定は [[tool.mypy.overrides]] 配下に module 名を指定する形で定義します。

https://mypy.readthedocs.io/en/stable/config_file.html#using-a-pyproject-toml-file

モジュール名自体はワイルドカード指定できるので、特定のモジュール群に対してまとめて設定を適用することも可能です。

pyproject.toml
[tool.mypy]
python_version = "3.9"
mypy_path = ["src"] 

[[tool.mypy.overrides]]
module = 'google.cloud.*'
ignore_missing-imports = true

linter / formatter などの各種ツールを紹介してきましたが、最後にツールの自動フックや実行周りの設定についても触れます。

pre-commit

これまで取り上げたようなツール群をまとめて実行するためのツールが pre-commit です。

https://pre-commit.ci/

.pre-commit-config.yamlに hook する対象ツールの定義を書き、pre-commit installを実行することで Git 標準の pre-commit hook として利用できます。

.pre-commit-config.yaml

repos:
    - repo: https://github.com/pycqa/isort
      rev: 5.10.1
      hooks:
        - id: isort
    - repo: https://github.com/psf/black
      rev: 21.11b1
      hooks:
        - id: black
    ...

一方で、pre-commit run --all-filesのような形で手動実行もできるので、都度実行の場合は Makefile にタスクとして定義しておくのが良さそうです。

https://pre-commit.com/#4-optional-run-against-all-the-files

.PHONY: lint
lint: ## Run linter and formatter
    .venv/bin/pre-commit run --all-files

終わりに

ざっと、linter / formatter を整備した時の内容をまとめてみました。

一通り整備していると正直セットアップだけでコストがかかるので、今であれば pysen などの整備用のツールを利用するのが良さそうと思います。
https://tech.preferred.jp/ja/blog/pysen-is-the-new-sempai/

Discussion