Gunosyデータ分析ブログ

Gunosyで働くデータエンジニアが知見を共有するブログです。

その設定、pyproject.tomlに全部書けます

はじめに

Gunosy Tech Lab - Media ML のsuchidaです。 最近はPythonの型アノテーションがないとあたふたする人生です。 こちらの記事は Gunosy Advent Calendar 2021の3日目の記事です。 前回の記事はid:skozawa さんの施策の優先順位付けのために分析Dayを実施しましたでした。

さて皆さん、Pythonを利用する際に静的解析ツールを利用していますか?これは、コードを綺麗に保ち、保守運用を行いやすくするために非常に重要なツールです。 本記事では、Pythonコードフォーマッターなどの静的解析ツールとその設定ファイルの管理方法について簡単に紹介します。

静的解析ツールの導入

チーム内では主に、アルゴリズムの開発・運用にPythonを利用しています。 また、コードを綺麗に保ち、保守運用を行いやすくするためにコードフォーマッターなどの静的解析ツールを導入しています。 なかでも、以下に挙げたツールがよく利用されています。

  • flake8: 文法チェックを行うツール
  • black: ソースコードを自動で整形するツール
  • isort: import文を自動で整頓するツール
  • mypy: 型アノテーションをもとに型チェックを行うツール
  • pytest: テストコードを検証するツール

また、パッケージ管理にはPoetryがよく利用されているので、Poetry前提で話しますが、以下のコマンドで各ツールをインストールすることができます。

poetry add -D flake8 black isort mypy pytest

また、各ツールの設定をカスタマイズする場合は、コマンドのオプション引数に記述したり、設定ファイルを追加する必要があります。 例えば、flake8の設定ファイルであれば、.flake8やsetup.cfgといった名前のファイルに以下のような設定を追加する事でオプションをカスタマイズすることができます。

[flake8]
max-line-length = 120
max-complexity = 18
ignore = E203, E266, W503
閑話休題

ちなみに、私が社内で見てきたオプション設定の書き方は以下のような変遷を辿っていたかと思います(たぶん)。

  1. コマンドの引数に直接オプションを渡す時代(それをREADMEにベタ書きする)
  2. Makefileにオプション付きのコマンドをまとめておく時代
  3. 各オプションを記述した設定ファイルを置く時代
  4. なるべくsetup.cfgにまとめた時代

1,2の方法で不便なのは、VSCodeなどのエディターの自動フォーマットなどの設定にレポジトリごとの設定を反映できない点です。 3,4の方法も悪くないですが、個人的にルートディレクトリのファイル数は減らしたいお気持ちがあります。特に3の方法だと、前述した5つのツールの設定が必要であれば5つのファイルが必要になり、それらに紛れて重要度の高いファイルを見つけづらくなります。

今回紹介するのは、オプション設定をpyproject.tomlにまとめる方法です*1

その設定、pyproject.tomlに全部書けます

前節に挙げたツールの設定は、以下のようにpyproject.tomlにすべて記述することができます。

[tool.poetry]
name = "example"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.9"

[tool.poetry.dev-dependencies]
black = "^21.11b1"
isort = "^5.10.1"
pyproject-flake8 = "^0.0.1-alpha.2"
mypy = "^0.910"
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.black]
target-version = ['py39']
line-length = 120

[tool.isort]
line_length = 120
multi_line_output = 3
include_trailing_comma = true
known_local_folder=['config',]

[tool.flake8]
max-line-length = 120
max-complexity = 18
ignore = "E203,E266,W503,"

[tool.mypy]
python_version = "3.9"
no_strict_optional = true
ignore_missing_imports = true
check_untyped_defs = true

[tool.pytest.ini_options]
testpaths = ["tests",]
filterwarnings = ["ignore::DeprecationWarning",]

厳密には、現状flake8がpyproject.tomlに対応していない(issue)ので、そのラッパーツールであるpyproject-flake8に置き換えてインストールしています。(flake8を走らせる際は、pflake8で実行します。)
mypyは比較的最近対応されたようです(issue)。

また、以下のようなMakefileを記述しておけば、一括でコマンドを実行することも可能なので、おすすめです。

.PHONY: tests
tests: ## run tests with poetry
    poetry run isort .
    poetry run black .
    poetry run pflake8 .
    poetry run mypy .
    poetry run pytest

またVSCodeの場合は、保存時に自動でフォーマッターなどを実行することができるのですが、嬉しいことに、pyproject.tomlの中の設定を勝手に参照して実行してくれます。black, isort, mypyについては手元で確認がとれました。flake8についてもpflake8との連携ができるようですが、blackで概ね対応できるので、スルーしています。
ただし、VSCode側で以下のようにオプション設定を記述してしまっていると、以下で上書きされてしまい意図した挙動にならないので注意です。

"python.formatting.blackArgs": [
    "--line-length=80"
],

また最近知ったのですが、pysenとよばれるisort, black, flake8, mypyを一括設定できるツールも存在しており、導入するのもありかもしれません。 github.com

おわりに

本記事では、静的解析ツールの紹介とその設定ファイルの管理方法について簡単にまとめました。 一つでも参考になる点があれば幸いです。個人的には、VSCodeと自動で連携が取れる点がマイベストです笑。

次回はmocyutoさんのApach Hudiな話です、お楽しみに!!

*1:2番煎じ感はご容赦下さいmm