はじめに
雛様言ったあああああ!!(今週のアオのハコ感想) nikkieです。
タイトルへの私の答え(2025年1月時点)は、否です。
RuffはFlake8の要素を全て含んでいません。
ただこの点はPythonコミュニティには膾炙しておらず、Ruffを高く評価する声が多いことに乖離を感じています。
目次
Flake8とRuff
Pythonの静的解析ツールの話です。
Flake8というリンタとしてよく使われていたライブラリがあります。
Pythonのスタイルガイド(PEP 8)に沿っていない部分をFlake8が指摘してくれます1。
ここ数年でRust製のツールが登場してきました2。
Flake8の代替と自称するのがRuff
RuffはFlake8相当のリンタ機能に加え、フォーマッタ機能もサポートします。
またリンタとしてチェックするルールは、Flake8だけでなく、isort(import
文の順序)やpyupgrade(最新のPythonの書き方)などもカバーしています3
Rust製のRuffは高速に動作します。
また、Flake8, isort, pyupgradeはツールごとに抽象構文木を作りリントしていたためオーバーヘッドがありましたが、様々なリンタ機能を併せ持つRuffでは抽象構文木を一度だけ作ればよくオーバーヘッドを解消していると主張しています。
実際使ってみると、速さはすごいなと思います。
RuffはFlake8の全ての要素を含んでいるか?
では、RuffはFlake8の完全上位互換なのかというと、私の見地からは完全上位互換ではないです。
たしかにRuffは速いのですが、Flake8の要素を完全に複製していません。
中途半端なFlake8クローン + 高速をゴリ押し主張という、私にとっては正直やや扱いに困る存在です
(言い換えると、特に既存のプロジェクトでは、積極的に導入しようと私からは言いかねるということです)
- RuffはFlake8のいくつかのルールを未実装
- Ruffにはプラグイン機構がない
RuffはFlake8のいくつかのルールを未実装
Ruffに未実装なルールの例はW503とW504です。
ドキュメント https://docs.astral.sh/ruff/rules/#warning-w には記載がありません。
Flake8でこれらは排他的なルールです。
- Line break occurred before a binary operator (W503)
- Line break occurred after a binary operator (W504)
実装の優先度を下げるという判断は理解できないわけではありません。
しかしながら、Flake8にW503とW504のうちのW503を指摘させているプロジェクトの場合、単にRuffに置き換えるだけではリンタのチェックの一部を失うことになります。
これはデグレードだと思うので、そうならないような対応と合わせてRuffを提案する必要があるでしょう。
私がメンテしているライブラリ(SpeechRecognition)もW503を指摘させているのですが、Ruffを導入するための案を考える時間があったらそれを実装改善に使いたい感覚でして、後回しにしています。
Ruffにはプラグイン機構がない
(これは喉から手が出るほどほしいので、存在をご存じの方はぜひ教えてください)
私の気持ちを代弁しているコメントがありました。
https://github.com/astral-sh/ruff/issues/283#issuecomment-2329103310
Members of my team have been agitating for Ruff, but without support for custom rules it's a tough sell.
意訳 私のチームのメンバーはRuffを使うのを訴えますが、Ruffがカスタムルールをサポートしないのでは採用する気にさせるのは難しいです
そ れ な !!
Flake8はおのおのがプラグインを開発して拡張できることで、コミュニティで広く使われていたと私には見えています4。
一方Ruffは各自にプラグインを開発する手段を与える代わりに、Ruff本体へのルールの追加を案内しています。
https://docs.astral.sh/ruff/contributing/#example-adding-a-new-lint-rule
でもこの座組だと、プロジェクトの中でだけ使いたいローカルなルールをFlake8のように手元でのみ実装とはいかないですよね
終わりに
RuffはFlake8の全ての要素を含んでいないことを書きました。
- Flake8の全てのルールをRuffが実装しているわけではない(例えば、W503とW504はRuffは未実装)
- Flake8にあるプラグイン機構はRuffにはない
RuffがFlake8のルール全てやプラグイン機構を実装して登場していたら、話は簡単だったでしょう。
速いRuffを使う一択です。
今までの機能は問題なく使え、さらに実行時間が短くなるということは、置き換えない理由が見当たりません。
式年遷宮としてもうまいのではないでしょうか。
現実はRuffが中途半端に映ります。
高速 + Flake8の機能の中途半端な複製。
新規プロジェクトではRuffを選んで全然よいと思います。
しかし、既存のプロジェクトでは、Flake8より速いというだけの理由でRuffを推すのは、私は賛成しかねます(ここがコミュニティとの乖離)。
置き換えたときに、今までの機能は問題なく使えるという点を補強する必要があると考えています。
なおこの記事は、SpeechRecognitionに届いた「RuffはFlake8の上位集合だから入れるべき(超意訳)」というプルリクエストへの返歌としても書きました。
https://github.com/Uberi/speech_recognition/pull/806#issuecomment-2567044395
「complete superset」というのは、事実誤認ですよ
>>> # この記事をコード片で表すと >>> flake8 = {"E401", "E501", "W503", "plugin"} >>> ruff = {"E401", "E501", "isort", "pyupgrade"} >>> ruff >= flake8 False