😄

PythonのLinterとFormatter設定

2023/08/12に公開

Python の Linter や Formatter といってもさまざまな種類があります。

今回は、Linter に「flake8」、Formatter に「black」を VSCode で利用するための設定方法をまとめました。

その前に、その他パッケージについても簡単にですがまとめておきます。

Linter

pycodestyle

pycodestyle は、Python のコードが PEP8 に準拠しているかをチェックするための linter です。以前は pep8 という名前でしたが、文書の方の PEP8 と名前が同じでややこしいので、pycodestyle という名前に改名されました。

後述の flake8 に内包されています。

pyflakes

未使用の import 文や変数などの、pycodestyle には検出できない論理的なエラーのみを検出するようです。

後述の flake8 に内包されています。

pylint

pylint も Python の汎用的な linter の 1 つで、VSCode のデフォルトの linter はこれが使われています。

オプションが豊富で多くのチェック項目に対応でき、カスタマイズ性が高いです。

また、一般的なエラー、スタイル違反、リファクタリングの提案など、多方面にわたるフィードバックを提供してくれます。

その一方、初期設定が難しく、プロジェクトによっては多くの警告を出すため、その警告の多さに鬱陶しさもあります。

formatter

autopep8

autopep8 は PEP 8 スタイルガイドに準拠するように Python コードを自動的に整形します。

autopep8 は pycodestyle によって報告されるほとんどの書式問題を修正できる様なので、pycodestyle と相性が良いフォーマッターです。

しかしながら、black ほど厳密ではなく、一部のフォーマット選択が他のフォーマッターと異なることがあります。

yapf

Google が開発しているツールです。(あくまで公式製品ではないと謳っているのですが...)

カスタマイズ性が非常に高く、ベースのコードスタイルを pycodestyle や google 独自のスタイルから選ぶことができる他、多くの項目をオプションで設定できます。

今回導入するもの

flake8

flake8 は、以下の 3 つの lint ツールをまとめたラッパーです。

  • pycodestyle
  • pyflakes
  • mccabe

pycodestyle と pyflakes については前述の通りです。

必要最低限のエラーとスタイル違反のみを指摘してくれ、シンプルで直感的に使えます。

デメリットとして、pylint ほどの詳細な分析やフィードバックは提供してくれない事でしょうか。

black

最近人気となっている formatter です。

強制的なコーディングスタイルで、考えることなくコードをフォーマットでき、設定の選択肢が少ないため、シンプルに使えます。

デメリットとしては、コーディングスタイルのカスタマイズがほとんどできず、ある種のスタイルを強制するので、全てのチームが受け入れるかは検討が必要です。

この様に Linter、Formatter ともにシンプルに利用できるものを採用しました。

インストール

それぞれを pip でインストールします。

$ python<version> -m pip install flake8 black

使い方

flake8 は、下記コマンドの様に指定するファイル or ディレクトリをチェックします。

$ flake8 path/to/code/to/check.py
# or
$ flake8 path/to/code/

特定のエラーだけをチェックする場合は、--selectオプションでエラーを指定します。

$ flake8 --select E123,W503 path/to/code/

逆に無視も可能です。

$ flake8 --extend-ignore E203,W234 path/to/code/

black も同じ様に使います。

$ black {source_file_or_directory}
or
$ python -m black {source_file_or_directory}

--codeオプションに文字列を渡すことで、cli 上でフォーマットさせることも可能です。

$ black --code "print ( 'hello, world' )"
print("hello, world")

-l, --line-lengthオプションを渡すことで一行の文字数を指定できます。

デフォルトは 88 文字になっています。

--diffオプションを渡すことでフォーマットによってどの様な差分が発生するかを確認できます。

$ black test.py --diff
--- test.py     2021-03-08 22:23:40.848954+00:00
+++ test.py     2021-03-08 22:23:47.126319+00:00
@@ -1 +1 @@
-print ( 'hello, world' )
+print("hello, world")
would reformat test.py
All done! ✨ 🍰 ✨
1 file would be reformatted.

その他細かい使い方は下記を参考ください。

https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html

blackとflake8の競合について

例えば、最大文字数は black だと 88 文字になっています。これを flake8 でも 88 文字にしなければなりません。

そこで、プロジェクトのルートディレクトリに.flake8という名前の設定ファイルを作成します。

このファイルを使用して、flake8 の設定をカスタマイズします。

[flake8]
max-line-length = 88

また、black のスタイルによっては、flake8 の特定のルールと競合することがあります。これらのルールを.flake8 ファイルで無効にします。

[flake8]
ignore = E203, W503

ここでは、E203 と W503 という 2 つの警告を無効にしています。

これは、black が PEP 8 と微妙に異なる方法で演算子の前後にスペースを追加するためです。

E203についてもう少し深掘り

E203 は、flake8 のエラーコードであり、Whitespace before ':'(コロンの前の空白)という意味を持っています。

これは、スライスや辞書のような Python のデータ構造でコロンの前に空白がある場合に警告されるルールです。

my_list[1 : 5]

上記のコードでは、スライスのコロンの前後に空白があるため、E203 が発生します。

ただし、black は、PEP 8 のこの特定の部分については少し異なるアプローチを取っています。

black は、算術演算子の周りの空白を一貫して扱うため、上記のようなスライスの場面で空白を許容します。

具体的には、二項演算子の前後の空白を一貫しているものとして扱うための取り組みの一部として、このスタイルを採用しています。

このため、black でフォーマットされたコードは、E203 に違反する可能性があります。

したがって、black と flake8 を一緒に使用する場合、このルールを無視するのが一般的とされています。

最終的な.flake8ファイルは下記です。

[flake8]
max-line-length = 88
ignore = E203, W503

W503についてもう少し深掘り

W503 は、flake8 の警告コードの 1 つで、二項演算子の前に改行が行われましたという意味を持っています。

これは、二項演算子の前で改行する場合に発生します。

例えば、以下のようなコードが該当します。

result = (some_long_variable_name + another_very_long_variable_name
         - yet_another_long_name)

ここでの+や-の前に改行があるため、W503 の警告が発生します。

しかしながら、PEP 8(Python の公式スタイルガイド)は、過去のバージョンとの互換性のために古い指針を維持していますが、実際には二項演算子の前での改行を推奨しています。

これにはいくつかの理由がありますが、主に次のような利点が挙げられます。

  • 行の終わりに演算子があると、その演算子を見逃しやすくなる
  • 一般的に、行の先頭に演算子があると、複数行にわたる式を読む際に整理されていると感じられる

black はこの「新しい」スタイルを採用しています。したがって、black でフォーマットされたコードは、W503 に違反する可能性があります。

black と flake8 を一緒に使用する場合、W503 の警告を無視するのが一般的です。

VSCodeでの設定

VSCode で flake8 と black を設定する方法について、文献を参考にしていたのですがうまくいきませんでした。

理由として、下記ワーニングが出ました。

この設定は間もなく非推奨になります。Flake 8 拡張機能を使用してください。詳細については、https://aka.ms/AAlgvkb を参照してください。

バージョンアップによって設定方法が変わっている様です。

まずは、下記拡張機能をインストールします。

.flake8 ファイルを用意します。

[flake8]
max-line-length = 100
ignore = E203, W503

.vscode/settings.jsonを作成し、下記を記載します。

{
    "editor.formatOnSave": true,
    "flake8.args": ["--config=.flake8"],
    "[python]": {
        "editor.defaultFormatter": "ms-python.black-formatter"
    }
}

こうすることで、保存時にフォーマッター(Black)が起動します。

そして flake8 がインストールされていたら、自動的に Python コードを flake8 で解析することになっている様です。

flake8のエラー検知

def double_numbers(numbers):
    print("test")

    return [          x * 2 for x in numbers]


# テスト
nums = [1, 2, 3, 4, 5]
print(double_numbers(nums))

return のところで下記エラーを検知します。

whitespace after '['Flake8(E201)

この状態で、保存を実行すると black が実行されます。

def double_numbers(numbers):
    print("test")

    return [x * 2 for x in numbers]


# テスト
nums = [1, 2, 3, 4, 5]
print(double_numbers(nums))

isortも入れてみる

import文の順番を一括で変更、PEP8に準拠したimport文にしたい時に有効です。

ついでにこちらもインストールしてみましょう。

$ pip install isort

使い方は、isortコマンドで整形します。

isort  {file or directory}

VSCodeでも設定してみます。

{
    "editor.formatOnSave": true,
    "flake8.args": ["--config=.flake8"],
    "[python]": {
        "editor.defaultFormatter": "ms-python.black-formatter",
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        },
    },
    "isort.args":["--settings-file", "setup.cfg"],
}

setup.cfgというファイルを指定していますが、そのファイルには下記の様に記載します。

[isort]
profile=black

これは、isortのフォーマットとblackのフォーマットが競合しない様にする対策です。

GitHubで編集を提案

Discussion