CLOVER🍀

That was when it all began.

mypyの設定(チェックルール)を調べる

これは、なにをしたくて書いたもの?

Pythonソースコードを書く時にはできる限り型をつけていこうかなと思っているのですが、mypyについてはとりあえず
--disallow-untyped-defsをつけて型指定がない場合にエラーにしているくらいだったので、もう少しちゃんと設定というかどういうチェックを
するべきか見ておこうかなと思いまして。

mypyの設定

説明としてはこちらです。

The mypy configuration file - mypy 1.14.0 documentation

mypyの設定は以下から読み込まれます。

  • ./mypy.ini
  • ./.mypy.ini
  • ./pyproject.toml
  • ./setup.cfg
  • $XDG_CONFIG_HOME/mypy/config
  • ~/.config/mypy/config
  • ~/.mypy.ini

あとはコマンドライン引数ですね。

The mypy command line - mypy 1.14.0 documentation

環境

今回の環境はこちら。

$ python3 --version
Python 3.12.3


$ pip3 --version
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)

mypyの設定を見る

ひとまずmypyをインストールします。

$ pip3 install mypy
$ mypy --version
mypy 1.14.0 (compiled: yes)

ヘルプを見てみます。

$ mypy --help

今回は型チェックに関する内容が見たいので、見るべきはこのあたりですね。

Disallow dynamic typing:
  Disallow the use of the dynamic 'Any' type under certain conditions.

  --disallow-any-expr       Disallow all expressions that have type Any
  --disallow-any-decorated  Disallow functions that have Any in their signature after decorator transformation
  --disallow-any-explicit   Disallow explicit Any in type positions
  --disallow-any-generics   Disallow usage of generic types that do not specify explicit type parameters (inverse: --allow-any-generics)
  --disallow-any-unimported
                            Disallow Any types resulting from unfollowed imports (inverse: --allow-any-unimported)
  --disallow-subclassing-any
                            Disallow subclassing values of type 'Any' when defining classes (inverse: --allow-subclassing-any)

Untyped definitions and calls:
  Configure how untyped definitions and calls are handled. Note: by default, mypy ignores any untyped function definitions and assumes any calls to such functions
  have a return type of 'Any'.

  --disallow-untyped-calls  Disallow calling functions without type annotations from functions with type annotations (inverse: --allow-untyped-calls)
  --untyped-calls-exclude MODULE
                            Disable --disallow-untyped-calls for functions/methods coming from specific package, module, or class
  --disallow-untyped-defs   Disallow defining functions without type annotations or with incomplete type annotations (inverse: --allow-untyped-defs)
  --disallow-incomplete-defs
                            Disallow defining functions with incomplete type annotations (while still allowing entirely unannotated definitions) (inverse: --allow-
                            incomplete-defs)
  --check-untyped-defs      Type check the interior of functions without type annotations (inverse: --no-check-untyped-defs)
  --disallow-untyped-decorators
                            Disallow decorating typed functions with untyped decorators (inverse: --allow-untyped-decorators)

None and Optional handling:
  Adjust how values of type 'None' are handled. For more context on how mypy handles values of type 'None', see:
  https://mypy.readthedocs.io/en/stable/kinds_of_types.html#no-strict-optional

  --implicit-optional       Assume arguments with default values of None are Optional (inverse: --no-implicit-optional)
  --no-strict-optional      Disable strict Optional checks (inverse: --strict-optional)

Configuring warnings:
  Detect code that is sound but redundant or problematic.

  --warn-redundant-casts    Warn about casting an expression to its inferred type (inverse: --no-warn-redundant-casts)
  --warn-unused-ignores     Warn about unneeded '# type: ignore' comments (inverse: --no-warn-unused-ignores)
  --no-warn-no-return       Do not warn about functions that end without returning (inverse: --warn-no-return)
  --warn-return-any         Warn about returning values of type Any from non-Any typed functions (inverse: --no-warn-return-any)
  --warn-unreachable        Warn about statements or expressions inferred to be unreachable (inverse: --no-warn-unreachable)
  --report-deprecated-as-note
                            Report importing or using deprecated features as notes instead of errors (inverse: --no-report-deprecated-as-note)

Miscellaneous strictness flags:
  --allow-untyped-globals   Suppress toplevel errors caused by missing annotations (inverse: --disallow-untyped-globals)
  --allow-redefinition      Allow unconditional variable redefinition with a new type (inverse: --disallow-redefinition)
  --no-implicit-reexport    Treat imports as private unless aliased (inverse: --implicit-reexport)
  --strict-equality         Prohibit equality, identity, and container checks for non-overlapping types (inverse: --no-strict-equality)
  --extra-checks            Enable additional checks that are technically correct but may be impractical in real code. For example, this prohibits partial overlap in
                            TypedDict updates, and makes arguments prepended via Concatenate positional-only (inverse: --no-extra-checks)
  --strict                  Strict mode; enables the following flags: --warn-unused-configs, --disallow-any-generics, --disallow-subclassing-any, --disallow-untyped-
                            calls, --disallow-untyped-defs, --disallow-incomplete-defs, --check-untyped-defs, --disallow-untyped-decorators, --warn-redundant-casts,
                            --warn-unused-ignores, --warn-return-any, --no-implicit-reexport, --strict-equality, --extra-checks
  --disable-error-code NAME
                            Disable a specific error code
  --enable-error-code NAME  Enable a specific error code

Configuring error messages:
  Adjust the amount of detail shown in error messages.

  --show-error-context      Precede errors with "note:" messages explaining context (inverse: --hide-error-context)
  --show-column-numbers     Show column numbers in error messages (inverse: --hide-column-numbers)
  --show-error-end          Show end line/end column numbers in error messages. This implies --show-column-numbers (inverse: --hide-error-end)
  --hide-error-codes        Hide error codes in error messages (inverse: --show-error-codes)
  --show-error-code-links   Show links to error code documentation (inverse: --hide-error-code-links)
  --pretty                  Use visually nicer output in error messages: Use soft word wrap, show source code snippets, and show error location markers (inverse:
                            --no-pretty)
  --no-color-output         Do not colorize error messages (inverse: --color-output)
  --no-error-summary        Do not show error stats summary (inverse: --error-summary)
  --show-absolute-path      Show absolute paths to files (inverse: --hide-absolute-path)

ちなみに、設定ファイルに書く時には_区切りになるようです。

デフォルト値はドキュメントを見ないとわからないようですね。

The mypy configuration file - mypy 1.14.0 documentation

ただ、説明自体はコマンドライン引数の方が読んだ方がよいみたいです。

The mypy command line - mypy 1.14.0 documentation

--strictはドキュメントを見ても「ヘルプを見ること」としか書かれていないので、実際に見てみるとこんな感じでした。

  --strict                  Strict mode; enables the following flags: --warn-unused-configs, --disallow-any-generics, --disallow-subclassing-any, --disallow-untyped-
                            calls, --disallow-untyped-defs, --disallow-incomplete-defs, --check-untyped-defs, --disallow-untyped-decorators, --warn-redundant-casts,
                            --warn-unused-ignores, --warn-return-any, --no-implicit-reexport, --strict-equality, --extra-checks

Enable all optional error checking flags. You can see the list of flags enabled by strict mode in the full mypy --help output.

mypy 1.14.0では以下のフラグが有効になるようです。

  • --warn-unused-configs
  • --disallow-any-generics
  • --disallow-subclassing-any
  • --disallow-untyped-calls
  • --disallow-untyped-defs
  • --disallow-incomplete-defs
  • --check-untyped-defs
  • --disallow-untyped-decorators
  • --warn-redundant-casts
  • --warn-unused-ignores
  • --warn-return-any
  • --no-implicit-reexport
  • --strict-equality
  • --extra-checks

OSSプロジェクトではどういう設定をしているんでしょう?とFastAPIを見たところ、全体としてはstrictのみtrueにしてあり、
ディレクトリによってはオーバーライドしているものもある、という感じでした。

https://github.com/fastapi/fastapi/blob/0.115.6/pyproject.toml#L123-L140

カテゴリー 項目 説明 デフォルト値 strictに含まれているか?
動的型付けを許可しない disallow_any_unimported フォローされていないインポートから取得する型(Any)の使用を禁止する False
disallow_any_expr Anyを使う式を許可しない False
disallow_any_decorated デコーレーターによる変換後の関数シグネチャAnyが含まれることを禁止する False
disallow_any_explicit Anyの明示的な使用を禁止する False
disallow_any_generics 明示的な型パラメーターを指定しないジェネリック型の使用を禁止する False
disallow_subclassing_any Anyのサブクラス化を禁止する False
型のない定義と呼び出し disallow_untyped_calls アノテーションを持つ関数を型アノテーションなしでの呼び出しを禁止する False
untyped_calls_exclude 特定のパッケージ、モジュール、またはクラスで定義された関数を disallow_untyped_calls アクションから選択して除外する
disallow_untyped_defs アノテーションのない関数定義または不完全な型アノテーションのある関数定義を禁止する False
disallow_incomplete_defs 部分的に型アノテーションが付与された関数定義を禁止する。完全に型アノテーションがない場合は許容する False
check_untyped_defs False
disallow_untyped_decorators アノテーションつきの関数を型アノテーションなしのデコレーターで装飾することを禁止する False
NoneOptionalの扱い implicit_optional デフォルト値を持つパラメーターを暗黙的に T | None として扱う False
strict_optional Optionalの型とNone値のチェックを無効にする。つまり、Noneはすべての型と互換性があるものとして扱う True
警告の設定 warn_redundant_casts 冗長なキャストを使用すると警告する False
warn_unused_ignores 不要な# type: ignoreコメントがあると警告する False
warn_no_return 関数からreturn文が欠落している場合に警告する。例外は関数の戻り値の肩がNoneまたはAny、関数本体が空で抽象メソッドとしてマークされているか、プロトコルクラス内またはスタブファイル内にある、例外をスローするなどのreturnに制御が移らない場合 True
warn_return_any Any以外の型を戻り値として定義している関数からAnyを返そうとすると警告する False
warn_unreachable 到達不能または冗長であるコードを検出すると警告する False
エラーの抑制 ignore_errors 致命的なエラー以外を無視する False
その他の厳密性フラグ allow_untyped_globals 型指定のないグローバル変数を許容する False
allow_redefinition 変数を別の型で再定義することを許容する False
local_partial_types ローカル変数に対する部分型(Noneを部分型として扱うこと)を禁止する False(mypy daemonでは暗黙的にTrueになる)
disable_error_code 指定したエラーコードを無効にする。複数指定する場合はカンマで区切る
enable_error_code 指定したエラーコードを有効にする。複数指定する場合はカンマで区切る
extra_checks 技術的には正しいが実用的なコードでない可能性があるものに対して、追加のチェックを行う False
implicit_reexport モジュールにインポートしたものを、暗黙的にエクスポートする True no_implicit_reexportが有効になる
strict_concatenate ?(extra_checksのこと?) False
strict_equality 互換性のない型の間での等価チェック、一意性チェック、コンテナチェックを禁止する False
strict 特定のチェックが有効になる(この表の右端を参照) False
エラーメッセージの設定 show_error_context 各エラーの先頭に関連するコンテキストを付与する False
show_column_numbers エラーメッセージに列番号を表示する False
show_error_code_links 対応するエラーコードへのドキュメントのリンクを表示する False
hide_error_codes エラーメッセージからエラーコードを非表示にする False
pretty エラーメッセージを見やすくする False
color_output エラーメッセージを色付きで表示する True
error_summary エラーメッセージの後に短いサマリーを表示する True
show_absolute_path ファイルの絶対パスを表示する False
force_uppercase_builtins Python 3.9以降であってもエラーメッセージの表示では大文字を強制する False
force_union_syntax Python 3.10以降であってもエラーメッセージ内の共用型には | の代わりにUnion[]Optional[]を使用する False
その他 warn_unused_configs mypyの呼び出し時に処理されるファイルと一致しない、設定ファイル内のモジュールごとのセクションに対して警告する False

その他は型チェックには関係しなさそうでしたが、strictで有効になるwarn_unused_configsのみ載せました。

こう見ていると、最低限strictを有効にすればよいのでは?という気もしますね。

mypy.iniを設定してみる

というわけで、ここまでの内容を元にmypyの設定ファイルであるmypy.iniを作成してみます。

まずはこんな感じにしてみようかなと思います。今回はデフォルト値のままでいいものについては省略しました。

mypy.ini

[mypy]
strict = True
disallow_any_unimported = True
disallow_any_expr = True
disallow_any_explicit = True
warn_unreachable = True
pretty = True

uvでpyproject.tomlに書く場合はこうでしょうか。

[tool.mypy]
strict = true
disallow_any_unimported = true
disallow_any_expr = true
disallow_any_explicit = true
warn_unreachable = true
pretty = true

使っていて微妙に思ったら、適宜見直していこうと思います。

ところで、設定する項目名を間違えると警告してくれるのがいいですね。

こんな感じで。

mypy.ini: [mypy]: Unrecognized option: hoge = True

おわりに

mypyの設定について調べてみました。

せっかくなのでざっくりまとめつつという感じで進めてみましたが、だいたい雰囲気はわかった感じです。

ちゃんと使っていこうと思います。