こんにちは! 株式会社iimonでエンジニアをしている遠藤です。
今携わっているプロジェクトの Python バックエンドで、コミット時に mypy を走らせて型チェックをするようにしたいと思いました。
ただ、以前別のプロジェクトで同じことを試したときに、依存関係が不足してエラーにハマった経験があります。
そこで今回は、そのときのエラー例と、最終的に Docker イメージを使って解決できた方法 をまとめてみました。
※本記事は --ignore-missing-importsを オフ にしている前提で書いています。 このオプションをオンにすると未解決の import は無視されますが、型チェックの網羅性が落ちる点には注意が必要です。
pre-commitについて
pre-commit は Git のフックを便利に管理できるツールです。
コードフォーマット(black, isort など)
静的解析(flake8, ruff など)
型チェック(mypy)
といった処理をコミット前に自動で実行できます。
レビュー前に最低限のコード品質を担保できるのがメリットです。
公式 hook を使ったときのエラー事例
.pre-commit-config.yaml は次のような設定でした。
# .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.10.0 hooks: - id: mypy args: [--config-file=mypy.ini]
pre-commit はこの設定で独自の仮想環境を作り、その中で mypy を実行します。
しかし、この環境にはプロジェクト依存が入っていないため、次のようなエラーが出ます。
error: Cannot find implementation or library stub for module named "django" error: Cannot find implementation or library stub for module named "ninja"
これを回避するには additional_dependenciesに不足ライブラリを書き足す必要があります。
# .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.10.0 hooks: - id: mypy args: [--config-file=mypy.ini] additional_dependencies: - django-stubs - django-ninja
ただし、この方法には次のような課題があります。
依存が増えるたびに追記が必要
requirements.txt / pyproject.toml と二重管理になる
mysqlclientなど C拡張ライブラリはホスト環境にヘッダやmysql_configがないとインストール自体が失敗する
特に 3 つ目は開発者の環境によって動いたり動かなかったりして面倒でした。
macOS の例だと、
OSError: mysql_config not found
といったエラーが出るため、brew install mysqlなどでヘッダやライブラリを別途入れる必要がありました。
ここでは例として mysqlclient を挙げましたが、実際には他の C 拡張ライブラリや stub が原因になるケースもあります。 必ずしも必要になるわけでもないですが、どちらにしても「Docker の開発環境でそのまま mypy を走らせられれば楽そうだな」と考えるようになりました。
Docker イメージを使った解決方法
公式ドキュメントをちゃんと読んだら、なんだかそれっぽいことが書いてありました。
docker_image
dockerフックのより軽量なアプローチ。docker_image "言語" は、既存の docker イメージを使ってフック実行ファイルを提供します。
docker_image フックは、便利なことにローカルフックとして設定できます。
エントリは、使用する docker タグを指定します。イメージに ENTRYPOINT が定義されていれば、実行ファイルをフックするために特別なことは何も必要ありません。コンテナで ENTRYPOINT が指定されていない場合や、 エントリでエントリーポイントを変更したい場合は、 それを指定します。
https://pre-commit.com/#docker_image
要するに、docker_image 言語 という仕組みがあり、既存の Docker イメージを使ってフックを実行できるようです。
そこで「普段の開発で使っている Docker イメージの中で mypy を動かす」ようにしました。
つまり、pre-commit 独自の仮想環境を使わず、プロジェクト用にビルドしている Docker イメージをそのまま利用します。
書き直した.pre-commit-config.yamlは以下のようになりました。
# .pre-commit-config.yaml repos: - repo: local hooks: - id: mypy-check args: [--config-file=mypy.ini] name: Run mypy inside project Docker image language: docker_image entry: --entrypoint mypy myproject-api:latest types: [python]
repo: local
→ ローカル定義のフックlanguage: docker_image
→ Docker イメージを使って実行entry
→--entrypoint mypyでエントリポイントを上書きし、イメージ名myproject-api:latestを指定している。イメージ名は自分の環境に合わせてください。
これで依存不足のエラーがなくなり、快適に mypy を走らせられました!
注意点
イメージ側に mypy と依存ライブラリがインストール済みであることが前提です(Dockerfile の pip install で入れておく)
イメージ名はチームで統一されていることが前提です
stub パッケージなど型用ライブラリは、いずれにしても requirements.txt / pyproject.toml に追加が必要です
まとめ
pre-commit は便利ですが、独自の仮想環境で動くため依存不足のエラーに悩まされることがあります。
特に C 拡張ライブラリでは環境依存のトラブルも起きやすいです。
docker_image を使えば、普段の開発で使っている Docker 環境をそのまま流用できるため、このような課題をシンプルに回避できます。 あくまで私のケースですが、同じように「mypy が pre-commit でうまく動かない…」と悩んでいる方の参考になれば嬉しいです。
現時点では実際に業務で運用したわけではないので、今後も知見が溜まったら追記していく予定です。
最後までお読みいただき、ありがとうございました! もっといい方法や記事の内容に誤りなどありましたら、ご指摘いただけますと幸いです。
また、弊社ではエンジニアを募集しております。 ぜひカジュアル面談でお話ししましょう! ご興味ありましたら、ご応募ください! Wantedly / Green