Mypy 利用ノート¶
道具としての mypy に関する雑記帳にしたいが、Python 型注釈機能に関しても綴っていく。ページを二つに分割するのがもちろん望ましい。
概要¶
Mypy is a static type checker for Python.
型チェッカーというのは構文チェッカーの特殊なものであるとひとまず理解しておけばいい。
Type checkers help ensure that you’re using variables and functions in your code correctly. With mypy, add type hints (PEP 484) to your Python programs, and mypy will warn you when you use those types incorrectly.
Python 言語仕様には、変数、関数、クラス、等々のコード構成要素に対して、その型を専用形式で注釈を付けることが可能であるというものがある。実際に型ヒントをコードに付与するようになると実感できるが、Python コードの可読性が高まるという恩恵をも感じる。関数の引数リストを見るだけで、実引数の型に関する必要条件がわかるというのは読み書きにおいて大きい利点だ。Mypy はそのように注釈が付いたコードを解析、報告する道具の一つだと述べている。
Python is a dynamic language, so usually you’ll only see errors in your code when you attempt to run it. Mypy is a static checker, so it finds bugs in your programs without even running them!
型注釈付きコードを、それを実際に実行する前に検証することを mypy は目的とする。
インストール・更新・アンインストール¶
複数人で共用するプロジェクトの開発環境に mypy をインストールする事例では、そのプロジェクトの定める手順に従え。README や pyproject.toml
を読めば判明する。
自分が所有する作業用仮想環境にインストールするならば、愛用している仮想環境ツールがインストールコマンドを実装している場合にはそれを使え。私ならば Miniconda であるから、例えば次のようにする:
$ conda install -c conda-forge mypy
インストール手順の説明は以上だ。Mypy の更新、アンインストールの手順は、対応する条件におけるインストール手順に合致する手順を選べ。例えば conda を使っているのならば conda uninstall mypy
を走らせる。
See also
- Miniconda 利用ノート
conda の使用法はここに記した。
構成・カスタマイズ¶
プロジェクトの pyproject.toml
の専用区間またはファイル mypy.ini
にオプションを指定するのが普通の運用と考えられる。
ユーザー設定としてファイル $XDG_CONFIG_HOME/mypy/config
などにオプションを指定することも可能だ。プロジェクトのほうの指定ファイルが優先される。他にも既定パスが設けられているが、私が使うファイルはこの二つしかない(のでそれらしか述べない)。
Some flags support user home directory and environment variable expansion. To refer to the user home directory, use
~
at the beginning of the path. To expand environment variables use$VARNAME
or${VARNAME}
.
という便宜が図られているので、ユーザー固有の情報を環境変数の形で記載した
mypy.ini
をバージョン管理のリモートリポジトリーに安心して置きやすい。
mypy.ini
の例¶# Global options
[mypy]
# Specifies the location where mypy stores incremental cache info.
cache_dir = "$XDG_CACHE_HOME/mypy"
# Suppresses error messages about imports that cannot be resolved.
ignore_missing_imports = True
# Enable all optional error checking flags.
# (n.b. includes check_untyped_defs and disallow_untyped_defs options)
strict = True
利用者ノート
なるべく --strict
オプションを付けたい。これが有効にするオプションの集合は mypy --help
の出力から判断可能。オプションの集合は mypy を更新するたびに変化する可能性がある。
See also
- The mypy configuration file
公式文書の解説。
- XDG Base Directory 利用ノート
当ノートはドットファイルの配置方針として XDG Base Directory 仕様を採用している。
使用方法・コツ¶
前提として、mypy documentation の最初の方の記述は体得することとする。Mypy を上手く使うコツで最も有効であるのものは、適切な型注釈コードを与えることだ。
型注釈に関する定型コード¶
スクリプトにせよモジュールにせよ、Python ファイルのインポート区画は次のコードを含む:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
# E.g. from typing import Never, Self
この if
ブロックでは型注釈にしか必要でないものをインポートする。
型注釈に関する作法¶
Python コードに対する静的解析ツール Ruff を併用して、型注釈に関する諸規則をオンにするとよい。
See also
Ruff 利用ノート の次の節を見ろ:
pyupgrade (UP): UP{013,014}, UP040, UP045.
flake8-annotations (ANN)
flake8-type-checking (TC)
よく用いる注釈用型¶
型注釈で用いる型には、オブジェクトの型ずばりそのものである場合とそうでない場合がある。例として、自作関数のある引数の型として list[str]
を想定していても、論理的には Sequence[str]
や Iterable[str]
などが正しいなどという場合が考えられる。
モジュール typing では次をよく用いる:
Any
. 未知の型を意味すると覚えておけ。Final[]
Literal[]
,LiteralString[]
Never
,NoReturn
Self
. ただし、メソッドの第一引数をself: Self
と明示的に指示する必要はないことに気をつけろ。関数定義に可変長キーワード引数を多用する者なら
TypedDict
および関連するNotRequired[]
,ReadOnly[]
,Unpack[]
(==*
).
モジュール collections.abc で定義された汎用プロトコルは重要だ。リンク先文書の Collections Abstract Base Classes 節の表を理解しろ。特に Inherits from 列が重要だ。例えば MutableSequence
が必要なのに
Sequence
と書いたり、逆に Sequence
で十分なのに MutableSequence
と書いするのはダメだと心得ろ。
Callable[]
: e.g.Callable[[ArgType0, ArgType1, ...], ReturnType]
Generator[]
: e.g.Generator[YieldType, SendType, ReturnType]
Iterable[]
: e.g.Iterable[YieldType]
Mapping[]
: e.g.Mapping[KeyType, ValueType]
Sequence[]
: e.g.Sequence[ValueType]
書くべき型がわからないときの対処法¶
例えば with open(path, "r") as fp: ...
のようなコードが与えられていて、自分のコードで fp
を引数に取る関数を定義する必要があるとき、その注釈型をドキュメントをなるべく引かずに知りたいとする。
そのようなときには typing.reveal_type(fp)
をコード中に含ませて mypy に解析させる。すると次のように実際の型を示す:
reveal_type
呼び出し例¶$ cat reveal_type_test.py
1 from typing import reveal_type
2
3 with open("tmp.txt", "r") as fp:
4 reveal_type(fp)
5 ...
$ mypy --strict reveal_type_test.py
reveal_type_test.py:4: note: Revealed type is "_io.TextIOWrapper[_io._WrappedBuffer]"
Success: no issues found in 1 source file
この場合、型注釈としては typing.TextIO
を用いるのが正解となる。なお、この例では open
の第二引数の値によって fp
の型が変わる。
Caution
reveal_type
呼び出しが製品コードにあってはならない。
型を mypy に教える¶
下のコードは BeautifulSoup を用いた処理の断片だ。生の column.text
のままでは
mypy はこの型が str
であることが推論できず strip
呼び出しをエラーとみなす。こういう場合には例えば関数 typing.cast
呼び出しで対象部分をラップすると上手くいく:
typing.cast
で型を明示する¶def get_column_value(item: Tag, propname: str) -> str:
if prop := item.find(string=propname):
column = prop.find_next("td")
if isinstance(column, Tag):
return cast(str, column.text).strip()
...
コードを本質的に修正せずにエラーを黙らせる¶
応急処置として mypy を黙らせる方法を記す。最終的にはコードのほうを正しく書くことで沈黙させろ。
インラインコメント
# type: ignore[NAME]
をエラー行の末尾に挿れる。コマンドラインオプション
--disable-error-code NAME
を付けて実行する。
代用ファイル¶
型注釈の与え方として、これまで記してきたのは既存の Python ファイルに型を直接書き込む方式だった。これをインライン方式と呼ぶ。もう一つの与え方に、拡張子 .pyi の代用ファイルを設けるものがある。おそらく、特に、ライブラリーの機能が Python 以外の言語で実装されているなど、インライン型注釈が物理的に不可能である場合の代替法だと考えられる。
- 拡張子が .pyi のファイル
対応する名前のモジュールが含む型付きオブジェクトの型注釈を記したファイル。
- ファイル
py.typed
代用システムを採用しているという目印。
オーバーロード¶
If a function or method can return multiple different types and those types can be determined based on the presence or types of certain parameters, use the
@overload
mechanism defined in PEP 484. When overloads are used within a “.py” file, they must appear prior to the function implementation, which should not have an@overload
decorator. (Typing Python Libraries, <https://typing.readthedocs.io/en/latest/guides/libraries.html>)
リンク先を見ると組み込み型の機能における実装例を示している。スタブファイルというもので用いるのが通例で、私は当分は使わない。
統合¶
Mypy 機能を常用のコードエディターに統合しておくと、編集と実行でウィンドウを反復する回数が減り、作業効率が向上することが期待できる。
Visual Studio Code¶
Visual Studio Code 拡張に mypy を支援するものがある。詳細は VS Code のサイドバー EXTENSIONS パネルの検索結果欄からそれぞれのヘルプページを見ろ。
- Mypy
Matan Gover 氏による拡張パック。使用者環境の mypy を用いるもよう。
- Mypy Type Checker
Microsoft による拡張パック。これ自体に mypy が付属されているもよう。Python 拡張が別途必要。
どちらでも良さそうなので、前者の拡張をインストールしておく。インストールが完了したら Ctrl + , で構成ファイルを開いて編集する。GUI を使う場合には検索欄に mypy
と入力して項目を絞り込め。ユーザー用とプロジェクト用の区別に気をつけろ。
settings.json
構成例¶{
"mypy.configFile": "/path/to/mypy.ini",
"mypy.runUsingActiveInterpreter": true
}
See also
資料集¶
- mypy documentation
公式文書。First Step 章にあるページが重要だ。
- mypy - Optional Static Typing for Python
公式ブログ。About から開発陣の簡単な紹介を読むことが可能。Examples にあるコードにおける型注釈は現在の Python 仕様を用いれば、コメントでない形式で書ける。
- PEP 484 - Type Hints
Python 型ヒント仕様と考えられる。
- Static Typing with Python
Type System Reference を通読して型理論の中核概念を理解しろ。かなりの数の例コードで mypy がエラーを出すのがかなり気になるが。
- Pros and Cons of Type Hints
型ヒント機能にまつわる長所と短所の比較論考。Real Python 内記事。
- Static type checking
Python コード静的型チェックの何たるかを議論。Scientific Python Development Guide 内記事。
- The Comprehensive Guide to mypy
すばらしい記事。Python 側の仕様進化により、用いる型やモジュールが若干古いのには注意する。
- Mypy is a waste of time · Issue #11492 · python/mypy
静的型解析アンチに対する反論に学びたい。