PythonのLinter/Formatter設定

こんにちは!バックエンドを担当しているしゅがーです。

昨年7月にUniforceにエンジニアとして入社しました。

今回はLinter/Formatterの設定について書いてみようと思います。

背景

Uniforceバックエンドでは、主にVSCodeを用いpythonでの開発をしています。

例にもれずLinter/Formatterを設定して開発をしています。

昨年から開発メンバーも増え、そろそろ見直しを兼ねて再定義することになりました。

そこに合わせて型チェックをしたいと考え、mypyの利用をすることにしました。

これまでUniforceでは、IOの型をできるだけ明示しておりましたが、開発・改修の中で食い違いが発生しており、混乱の原因となるので一緒に是正したいと考えています。

選定

Ruff

これまでは、flake8 black isort を利用していました。

見直しをするにあたり、これらの定義を最新や他の先達に合わせていく方向でいいかなと最初は考えておりました。

しかし、念のため他への置換えも検索しRuff を発見しました。

こちらは下表にあるとおり、爆速な上に今まで使用していたflake8 black isort と同等のチェックがこれ1つでできます。

さらにページを見るとApache AirflowやFastAPI、Hugging Face、Pandas、SciPyなどのプロジェクトで採用されており、これからもしばらくは更新されていくだろうと予想されます。

上記から使わない理由がないので採用しました。

mypy

これまでは静的型チェックは利用しておらず、開発者それぞれがルールに沿ってIOを明示するよう型定義をしている状態でした。

そこで今回Linter/Formatterを再定義するにあたり、静的型チェックも合わせて実施しようと考えました。

検索したところmypyで設定している方が多く、知見も多くあったので採用しました。

導入

インストール

VSCodeを使用していること、割とそのまま使用できることから、VSCodeのExtensionsを利用して入れてます。

Extensionsから以下を検索することで使用できます。

下記のそれぞれのバージョンはUniforceでの2024年2月現在利用しているものです。

下記内容はこの指定バージョンでの利用を前提としています。

  • Ruff(v.0.2.1)
  • Mypy Type Checker(mypy: v.1.8.0)
  • (Even Better TOML(tomlファイルを編集する場合にハイライトやフォーマットが行える))

※もちろんpip等で入れる方法もあります。

参考:

https://qiita.com/siruku6/items/6a8412c41616b558df66

https://zenn.dev/yamasakit/articles/dc9ed1acd5ae3b

https://qiita.com/tamo_breaker/items/dbd8482e88c61269d531

設定ファイル

.vscode/settings.jsonに以下を追加します。

下記では設定ファイルのPath先と即時反映機能をONにしています。

即時反映でちょっと確認等で触ったファイルがフォーマットされてしまって修正箇所を明示し難いなどの場合は適宜"source.fixAll.ruff": false などして対応ください。

{
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports.ruff": true,
"source.fixAll.ruff": true
},
"editor.defaultFormatter": "charliermarsh.ruff",
},
"ruff.lint.args": [
"--config=${workspaceFolder}/pyproject.toml",
],
"mypy-type-checker.args": [
"--config=${workspaceFolder}/pyproject.toml"
],
}

定義

Ruffもmypyも同じtomlファイルで定義できることからまとめています。

現状として、業界標準に近づけようということで参考にさせていただいて下記としました。

Ruffの参考

mypyの参考

[tool.ruff]
# Set the maximum line length to 119
line-length = 119
target-version = "py38"
[tool.ruff.lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule.
select = ["ALL"]

[tool.ruff.format]
# Enable reformatting of code snippets in docstrings.
docstring-code-format = true

[tool.mypy]
allow_redefinition = true
allow_untyped_globals = false
check_untyped_defs = true
color_output = true
disallow_incomplete_defs = true
disallow_untyped_calls = false
disallow_untyped_decorators = false
disallow_untyped_defs = true
error_summary = true
ignore_missing_imports = true
implicit_reexport = true
namespace_packages = true
no_implicit_optional = true
pretty = true
show_column_numbers = true
show_error_codes = true
show_error_context = true
show_traceback = true
strict = true
warn_no_return = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
warn_unused_ignores = true

下記に設定についてChatGPTに解説してもらった結果を記します。

ruffの設定[tool.ruff]

  • line-length = 119: 1行あたりの最大文字数を119文字と設定しています。これは、コードの読みやすさを保つための一般的な規則です。
  • target-version = "py38"ruffがチェックする際に、Python 3.8の構文と機能を対象とすることを指定しています。

[tool.ruff.lint]

  • select = ["ALL"]ruffで全ての利用可能なリントチェックを有効にすることを指定しています。

[tool.ruff.format]

  • docstring-code-format = true: コメント内のコード(docstring)のフォーマットもチェックすることを有効にしています。

mypyの設定mypyは、Pythonコードの型チェックを行うためのツールです。型チェックを通じて、コードの正確さを向上させることができます。

  • allow_redefinition = true: 同じスコープ内での変数の再定義を許可します。
  • allow_untyped_globals = false: 型注釈のないグローバル変数を許可しない設定です。
  • check_untyped_defs = true: 型注釈のない関数の本体もチェックします。
  • color_output = true: エラーメッセージに色を付けて出力します。
  • disallow_incomplete_defs = true: 型注釈が不完全な関数定義を禁止します。
  • disallow_untyped_calls = false: 型注釈のない関数への呼び出しを許可します。
  • disallow_untyped_decorators = false: 型注釈のないデコレータを許可します。
  • disallow_untyped_defs = true: 型注釈のない関数定義を禁止します。
  • error_summary = true: エラーの要約を表示します。
  • ignore_missing_imports = true: 見つからないインポートに対するエラーを無視します。
  • implicit_reexport = true: 明示的な再エクスポートなしに、インポートされた名前を再エクスポートします。
  • namespace_packages = true: 名前空間パッケージの扱いを有効にします。
  • no_implicit_optional = true: 明示的にOptionalを指定しない限り、Noneが許容されないようにします。
  • pretty = true: エラーメッセージをより読みやすく整形して表示します。
  • show_column_numbers = true: エラーが発生した列の番号も表示します。
  • show_error_codes = true: エラーコードを表示します。
  • show_error_context = true: エラーのコンテキストを表示します。
  • show_traceback = true: エラーのトレースバックを表示します。
  • strict = true: 厳格な型チェックモードを有効にします。
  • warn_no_return = true: return文が必要な関数でreturn文がない場合に警告します。
  • warn_redundant_casts = true: 不要な型キャストに対して警告します。
  • warn_return_any = true: Any型を返すことを警告します。
  • warn_unreachable = true: 到達不可能なコードに対して警告します。
  • warn_unused_configs = true: 使用されていない設定に対して警告します。
  • warn_unused_ignores = true: 使用されていない# type: ignoreコメントに対して警告します。

これらの設定は、コードの品質と安全性を向上させるために、リントチェックと型チェックの厳格さを調整します。

最後に

まだまだ定義したばかりなので、少しずつまた修正を重ねていくことになるかと思います。

また、近いうちに以下の利用を考えています。

下記を利用することで常にリポジトリは一定のフォーマットを保つことができます。

pre-commit

コミット時にチェックしてエラーならコミットできない仕組み

設定方法

Ruff、mypyのインストール

pip install ruff==0.2.1 mypy==1.8.0

pre-commitのインストール

pip install pre-commit

pre-commitの有効化

pre-commit install

pre-commitの無効化

pre-commit uninstall
  • pre-commit設定ファイル例 配置先はプロジェクト直下 ファイル名: .pre-commit-config.yaml
    repos:
    # Ruff
    - repo: <https://github.com/astral-sh/ruff-pre-commit>
    rev: v0.2.1
    hooks:
    # Run the linter.
    - id: ruff
    name: ruff
    description: "Run 'ruff' for extremely fast Python linting"
    entry: ruff check --force-exclude
    language: python
    types_or: [python, pyi]
    # --fix: enable lint fixes
    args: [--fix]
    require_serial: true
    additional_dependencies: []
    minimum_pre_commit_version: "2.9.2"
    # Run the formatter.
    - id: ruff-format
    name: ruff-format
    description: "Run 'ruff format' for extremely fast Python formatting"
    entry: ruff format --force-exclude
    language: python
    types_or: [python, pyi]
    args: []
    require_serial: true
    additional_dependencies: []
    minimum_pre_commit_version: "2.9.2"
    # mypy
    - repo: <https://github.com/pre-commit/mirrors-mypy>
    rev: v1.8.0
    hooks:
    # Run the mypy.
    - id: mypy
    name: mypy
    description: "Run 'mypy' for Python linting"
    entry: mypy
    language: python
    args: [--strict, --ignore-missing-imports]
    require_serial: true
    # Add types package list
    additional_dependencies: []
    minimum_pre_commit_version: '2.9.2'

参考:

https://zenn.dev/nowa0402/articles/79aaeb8db5731c

https://pre-commit.com/#intro

GitHub Actions

Github-actionsでRuffを利用することも出来ます。指定の設定に従ってlintルール違反のチェックをすることが出来ます。詳細は下記から

参考:

https://docs.astral.sh/ruff/integrations/#github-actions

https://qiita.com/ciscorn/items/fd58f63c602a3fdca632

最後まで読んでいただきありがとうございました!