dorapon2000’s diary

忘備録的な。セキュリティとかネットワークすきです。

flake8のE125とE129の扱いについて

pythonのコーディング規約としてPEP8はとても有名です.そして,pycodestyleはコードがPEP8に準拠しているかチェックし,不適切であれば対応したエラーコードを示すツールです.pythonを使っている方にはお馴染みかもしれないflake8でも,内部でpycodestyleを使っています.

問題はflake8(正確にはpycodestyle)のエラーコードE129と大元のPEP8に矛盾があるのではないかということです.また,E125とE129の関係も含めて,調べてみました.

E125とは

Continuation line with same indent as next logical line

継続行のインデントレベルが次に続く論理行と同じインデントレベルになっている

# bad
if user is not None and user.is_admin or \
    user.name == 'Grant':
    blah = 'yeahnah'


# good
if user is not None and user.is_admin or \
        user.name == 'Grant':
    blah = 'yeahnah'

E129とは

Visually indented line with same indent as next logical line

視覚的に統一するためのインデントが次に続く論理行と同じインデントレベルになっている

# bad
if (row < 0 or module_count <= row or
    col < 0 or module_count <= col):
    raise Exception("%s,%s - %s" % (row, col, self.moduleCount))

# good
if (row < 0 or module_count <= row or
        col < 0 or module_count <= col):
    raise Exception("%s,%s - %s" % (row, col, self.moduleCount))

PEP8におけるインデントの例

if 文の条件部分が、複数行にまたがって書かなければならないくらい十分に長い場合があります。この場合、2文字のキーワード(つまり、 if) の後にスペースを一つ置き、開き括弧を置くと、2行目以降の条件部分は通常スペース4つ分インデントされることになります。 if 文の中でネストされるインデントされたコードも通常スペース4つ分インデントされるので、ネストされたコードの固まりと条件部分が見た目上区別がつかなくなってしまう可能性があります。 この PEP は、 if 文に含まれるネストされたコードの部分と、継続された条件部分を区別するかどうか(またはどうやって区別するか)については立場を示しませんが、許容できるやり方はいくつかあります:

# 追加のインデントをしない
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# シンタックスのハイライトをサポートするエディタで区別するため
# コメントを追加する
if (this_is_one_thing and
    that_is_another_thing):
    # 両方の条件がtrueなので、処理を調整可能
    do_something()

# 継続された行の条件をインデントする
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

https://pep8-ja.readthedocs.io/ja/latest/#id5

要約すると,if文の条件文と直後にある論理文のインデントが揃うとみづらいけど,直してもいいし直さなくてもいいよ.ということです.

E129とPEP8の矛盾

E129では,if文の条件分と直後の論理文のインデントが揃うのはNGとしていますが,PEP8ではNGとしていません.ここで矛盾が発生します.どちらを信じればいいのでしょう.とりあえず,どちらの制約も守りたいというならば,「# 継続された行の条件をインデントする」のやり方をすれば解決します.しかし,個人的には嫌いです.

if (is_aaaaaaaaaaaaa
    and is_bbbbbbbbbbbb
        and is_cccccccccccccc):
    do_something()

条件文が3行に渡る時,最後だけインデントが深くなるのが気になって夜も寝られません.

矛盾が発生した経緯

github.com

上記のIssueの中で説明されていました.

sigmavirus24 commented on 3 Feb 2016

Please understand that the document is frequently updated and this particular section that you've emphasized was written after E129 was created. Also, this tool is being rebranded to avoid any further confusion of its checks with PEP-0008 the document.

以前はPEP8に準拠していたE125が,PEP8の更新にともない矛盾する形になったため,矛盾するif文の部分だけE125から分離してE129として作成し,矛盾が気になるならユーザ自身でE129を無効化できるようにした.ということらしいです.コミュニティはちゃんと矛盾を認識して対応させていたのですね.なぜ,pycodestyleはデフォルトでE129を無効にしないのかという疑問は,以下で回答されています.

sigmavirus24 commented on 3 Feb 2016

We add very few checks per minor release and try to disable very few by default. If we keep shifting checks into disabled by default or on by default and adding new checks, we're making the tool actively user-hostile. When people run pep8/pycodestyle/flake8 in their CI to check for style consistency and your example code stops returning a warning when people are expecting it to (and feel that the style pycodestyle is enforcing is better) then you're being actively hostile towards your users.

すでにCIでE129が検知されることが前提となっているテストが多く存在するため,無効化することは難しいようです.


では,いつ矛盾が発生し,いつ認知され,いつE125からif部がE129として分離したのか,時系列で追っていきます.

  • [2011-06-03] PEP8に複数行に渡る文のインデントについての記述が追加される.
  • [2012-06-01] pycodestyleにPEP8とは合い慣れないE125が実装される.
  • [2012-09-27] E125とPEP8がif文にて矛盾することがIssue#126にて指摘される.
  • [2013-03-22] 矛盾部分だけ無効化できるように.E125のif部をE129として分離.
  • [2014-04-28] PEP8にif文のインデントの書き方の具体例が加わる(上で説明した# 追加のインデントをしない).これにより明白にPEP8とE129が矛盾することに.

なるほど.

これで,E129は別に無視してもPEP8違反にはならないことがわかりました.とは言うものの,PEP8で推奨しているわけでもなく,みづらいことに変わりはないので注意が必要です.

【おまけ】VSCodeのflake8とautopep8でE129を無効化する

setting.json

    "python.linting.enabled": true,
    "python.linting.flake8Enabled": true,
    "python.linting.flake8Args": [
        "--ignore=E129",
    ],
    "python.formatting.provider": "autopep8",
    "python.formatting.autopep8Args": [
        "--ignore=E129"
    ],

参考

pycodestyle(pep8) エラーコードチートシート - Qiita

pep8 が pycodestyle に変わった話 - Qiita

E129 is wrong and misleading · Issue #474 · PyCQA/pycodestyle · GitHub

E125 overreaches pep8 · Issue #126 · PyCQA/pycodestyle · GitHub