皆さまこんにちは、ウチイダです。

時間を見つけてはDjango で中規模以上のアプリケーションを開発するための環境構築について調べています。

前回の記事はこちら。

ウチイダの作業環境は以下です。

  • Windows 11 21H2
  • WSL2 Ubuntu20.04
  • GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)

以降の内容は、この環境にPython/Djangoの開発環境を導入していく手順メモとなります。

転ばぬ先の型チェック

今回は、型チェッカーを導入します。

TypeScript + React をやり始めてから、実行時エラーで悩むことが激減しました。

Pythonでもそういう開発体験ができるといいなあと思って、型チェッカーを使っていくことにしました。

Pythonでは、TypeScriptのような静的型付けの機能を持ったスーパーセット言語はなさそうですが、型ヒント(Type hinting)が機能としてついているので、これを使うと型不整合を検出できるようです。

型の不整合をチェックするツールとしては、mypy がメジャーなようなので、これを導入していきます。

http://mypy-lang.org/index.html

エラーチェックツールとしては、VSCodeの拡張機能であるPylance も人気があります。

しかし、こちらはあくまでVSCode上でチェックをしてくれるもので、CLIで任意のタイミングに実行することができません。

チーム開発で使うことを考えて、Gitのpre-commitやレビュー実施時にCLIから実行をすることを考えて、mypyを採用します。

おことわり

mypy の導入にあたってはスムーズに行かなくてけっこう試行錯誤しました。

うまくいかない経緯も何らかのお役に立てるかもしれないと思い、なるべくそのまま掲載しています。

シンプルな導入手順を知りたい方にとっては冗長かもしれません。そのうちきれいに手順をまとめようと思います。

mypy のインストール

Poetry で導入する場合の例です。

$ poetry add --group dev mypy
The --dev option is deprecated, use the `--group dev` notation instead.
Using version ^0.991 for mypy

Updating dependencies
Resolving dependencies... (0.3s)

Writing lock file

Package operations: 2 installs, 0 updates, 0 removals

  • Installing typing-extensions (4.4.0)
  • Installing mypy (0.991)

pip でインストールする場合は以下のようにコマンドを実行します。

$ pip install mypy

これだけで導入は完了です。

CLI からチェックを試す

ためしに、あえて型不整合があるスクリプトを書いて、それをチェックしてみます。

import datetime

var: datetime.datetime = "foo"

mypy でのチェックは、mypy <対象ファイル> で行います。

$ mypy mypy_test.py
mypy_test.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "datetime")  [assignment]
Found 1 error in 1 file (checked 1 source file)

datetime と str で型エラーが出ました!

ところで、上記のスクリプトって実行時エラーにもならないんですね…

VS Code 上で警告を表示する

続いて、VS Codeのエディタ上に型不整合のエラーを表示していきます。

VS Code上の設定項目にmypy が入ってるので、これを有効にします。

Mypy Enabled にチェックを入れる

ウチイダこれだけでOKでした。

さらに、プロジェクト全体を自動でチェックしてくれるように、Mypyの拡張機能も導入してみます。

上記の設定だと、ひらいてるファイルのチェックしかしてくれないようなんですよね…

mypy をシステムグローバルにインストールしていない(プロジェクトごとの仮想環境にしかインストールしていない)場合、mypy daemon が見つからない、という警告が出る場合があります。

mypy daemon が見つからないエラー

公式ドキュメントを見てみると、mypy server プロセスを起動させて置きそれに型チェックをさせるしくみがあるようです。

VS Codeはこれと連携して、チェックをしているみたいですね。

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

さらに、mypy の拡張機能の説明文を読んでみると、仮想環境上のmypy を利用する場合は、 mypy.RunUsingActiveInterpreter をEnable にするように、と書いてありました。

仮想環境のmypy を使う場合の指示

設定をしたところ、エラーが消えました!

Dmypy.Executable はデフォルト値のままでOKです。

これで基本の設定は完了です。

返り値の型指定がない関数の内部はチェックされない

基本の設定は完了…と思っていたのですが、まだもう少し続きます。

どんなパターンでエラーが出るのか、いろいろ試していたところ、間違っていそうなのにVS Code で警告されないものがありました。

str 型の変数に、わざわざint キャストした整数値を代入しています。

def main():
    num_string: str = int(2)
    print(f"test: {num_string}")


if __name__ == "__main__":
    main()

それでも、エラーになりません。

エディタ上でも、CLIでもエラーにならない

よく見ると noteのところに、untyped な関数の内部はデフォルトではチェックしないので、 –check-untyped-defs を使ってみて、と書いてあります。

オプションを加えてチェックを再実行したところ、エラーになりました。

–check-untyped-defs オプションをつけるとエラーになる

untyped function というのは、返り値の型を定義していない関数のことのようです。

というわけで、 main() の返り値としてNoneを指定してみます。

返り値を設定したら、エラー検出された

untyped function ではなくなったので、エディタ上でも検出してくれました。

VS Codeでのチェックにオプションを設定する

型注釈がない関数が、VS Code上で警告されないのはつらいです。

調べてみたところ、VS Code の設定から、 mypy のチェックオプションを設定できるようでした。

設定画面を開いて、 python.linting.mypyArgs から設定できます。

ひとまず、–check-untyped-defs を追加しました。

python.lintinh.mypyArgs に –check-untyped-defs を追加

すると、 main() の返り値に型注釈がなくてもエラーを検出してくれました!

unyped-function の内部も、エディタ上で警告されるようになった

mypy のチェックオプション

オプションとして設定できる項目はいろいろあるようです。

https://mypy.readthedocs.io/en/latest/command_line.html#specifying-what-to-type-check

Python の型ヒントの仕様とあわせて理解すれば、良い感じの制限を作れそうです。

VS Code の設定を共有する

VS Code をチームで統一して使っている場合、その動作も統一したくなります。

mypy はけっこう設定の作業項目が多いので、可能なら全員で同じ設定ファイルを共有してしまうのがよいと思います。

プロジェクト内に .vscode/settings.json を作って、Git リポジトリに共有するだけでいいのでお手軽ですね。

mypy の設定は、json でみると今のところこんな感じになっています。

{
  "python.linting.mypyEnabled": true,
  "mypy.runUsingActiveInterpreter": true,
  "python.linting.mypyArgs": [
    "--follow-imports=silent",
    "--ignore-missing-imports",
    "--show-column-numbers",
    "--no-pretty",
    "--check-untyped-defs"
  ]
}

もう少しきちんと設定ファイルを作りたいです。。。

まとめ

とりあえずCLIと、VS Code上での型不整合の検出ができるようになりました。

TypeScriptもそうですが、設定周りで覚えることが多いので、もっと使い慣れていく必要があるなと思いました…

というわけで、今回はここまで。

あなたのお役に立てればうれしいです。

参考資料

https://ohke.hateblo.jp/entry/2020/10/03/230000

https://mypy.readthedocs.io/en/latest/mypy_daemon.html

https://future-architect.github.io/articles/20201223/

https://mypy.readthedocs.io/en/latest/command_line.html#specifying-what-to-type-check