Skip to content

pre-commit の flake8 フックが新規開発環境で常に失敗する(flake8==3.6.0 と Python 3.8+ の非互換) #762

Description

@chiku-samugari

概要

.pre-commit-config.yaml で固定されている flake8==3.6.0(2018 年リリース)は、Python 3.8 以降で構築された pre-commit のフック環境では動作しません。このため、新規にセットアップした開発環境では .py ファイルを含むコミットが pre-commit フックで必ず失敗します。

再現手順

  1. 新規の開発環境で pre-commit をインストールし(確認時は pre-commit 4.2.0)、リポジトリで pre-commit install を実行する。このときフック環境はローカルの新しい Python(確認時は 3.12.9)で構築される
  2. 任意の .py ファイルを変更してコミットを試みる
  3. flake8 フックが対象ファイルの内容に関係なくクラッシュし、コミットが失敗する

発生するエラー

表面上は flake8 内部の KeyError として報告されます:

  File ".../flake8/exceptions.py", line 86, in __init__
    self.original_exception = kwargs.pop("exception")
KeyError: 'exception'

これは flake8 3.6.0 の checker.py にあるタイポ(excetion=all_exc)により本来の例外が隠されているためで、実際の原因は以下です:

  File ".../pyflakes/checker.py", line 700, in getNodeHandler
    self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
AttributeError: 'FlakesChecker' object has no attribute 'CONSTANT'

原因

  • flake8==3.6.0 が依存する pyflakes 2.0.0 は、Python 3.8 以降ですべてのリテラルが ast.Constant ノードとして表現される仕様変更に対応していません(対応は pyflakes 2.2 以降)。そのため Python 3.8+ のフック環境では、どの .py ファイルを解析してもクラッシュします
  • 古い Python でフック環境を構築するという回避も現在は不可能です。最新の pre-commit に同梱される virtualenv は Python 3.7 以前のインタプリタを扱えません(language_version: python3.6 を指定しても環境構築自体が失敗します)

影響範囲と、これまで顕在化しなかった理由

  • 新規セットアップの環境では .py ファイルを含むコミットが必ず失敗します
  • 一方、以下のいずれかに該当する既存環境では顕在化しません
    • 過去に Python 3.7 以前でフック環境を構築済み(pre-commit は環境をキャッシュし続けるため)
    • そもそも pre-commit install を実行していない(フックは clone ごとのオプトイン)
    • --no-verify でコミットしている

回避策

SKIP=flake8 git commit ...

で flake8 フックのみをスキップできます(他のフックは通常どおり実行されます)。lint 自体は、新しい flake8 をリポジトリルートで実行すれば setup.cfg[flake8] 設定が適用されるため手動で確認可能です。

修正案

upstream の CenterForOpenScience/osf.io では 2024 年 7 月の "Great Big Python Upgrade"(CenterForOpenScience#10648)で flake8 を 7.0.0 へ更新し、新たに検出される lint エラーの修正も併せて行っています。これに追随するのが本筋と思われます。

なお、flake8 フックは pre-commit/pre-commit-hooks リポジトリからは v2.0.0 で削除されており、現在は PyCQA/flake8 リポジトリのフックを直接利用する形が推奨されています。

確認環境

  • リポジトリ: release/26.07.08(5c1b778)
  • pre-commit 4.2.0(Python 3.12.9 / pyenv)
  • OS: Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions