- Rust製のPythonリンター・フォーマッター Ruff v0.16.0 は、デフォルトで有効なルールを59件から413件へ増やし、追加設定なしで構文エラーや即時に発生するランタイムエラーをより幅広く検出できるようになった
- Markdownで
python、py、pyi、pycon などと指定された Pythonコードブロックのフォーマット をサポートし、Quartoノートブックにも適用できる
ruff: ignore と ruff: file-ignore が追加され、論理コード行やファイル全体の診断を抑制できるほか、--add-ignore でコメントを自動挿入できる
check と format --check は修正内容を通常の診断の下に diffで表示 し、フォーマッターの検査でもJSONやGitHub・GitLab CIコメント向けの出力形式をサポートする
- 多くの場合は大きな変更なしでアップグレードできるが、増加したデフォルトルールと、一部の値が
null になりうる JSON出力の変更 が既存設定や自動化ツールに与える影響は確認が必要
デフォルトルールを413件に拡大
- Ruff v0.16.0 はRust製の高速なPythonリンター・フォーマッターで、PyPIまたは
uv tool install ruff@latest でインストールできる
- Ruffの全ルール数はv0.1.0当時の708件から968件へ増えたが、デフォルトで有効なルールはこれまで59件のままだった
- v0.16ではデフォルトルールを 413件 に拡大し、構文エラーや即時に発生するランタイムエラーを含む深刻な問題を追加設定なしで検出する
- flake8-bugbearの
B、pyupgradeの UP、Ruff独自の RUF カテゴリーのルールなどが含まれる
- 全一覧は Default Rules ドキュメントで確認できる
- すでに
select や extend-select を使っているプロジェクトでも、新しいデフォルトルールによってこれまで気づかなかった有用なルールを確認できる
- 以前のデフォルトルールに戻すには、次のように設定する
[lint]
select = ["E4", "E7", "E9", "F"]
- 今回の変更は長期課題である ルール再分類 とも関連しており、関連作業は今後も続く予定
Markdownコードブロックのフォーマット
ruff format がMarkdownファイルに含まれる Pythonフェンスコードブロック をフォーマットする
- サポートする情報文字列は
python、py、python3、py3、pyi、pycon である
pyi はスタブファイル形式として扱う
pycon はREPLセッション形式として扱う
- それ以外は通常のPythonファイルのようにフォーマットする
{python} のように言語名が波括弧で囲まれていても認識するため、Quartoノートブック にも使える
.qmd 拡張子を使う場合は extension マッピング設定が必要になることがある
- コードブロック内では
fmt: off と fmt: on で一部のフォーマットを抑制できる
- Markdown文書領域全体は
<!-- fmt: off --> と <!-- fmt: on --> のHTMLコメントで除外できる
- すべてのMarkdownファイルを除外するには、
extend-exclude に *.md のようなglobを指定する
- 詳細な挙動は Markdownコードフォーマットのドキュメント で確認できる
新しい診断抑制コメント
- v0.15の
ruff: disable・ruff: enable による範囲抑制に続き、v0.16では ruff: ignore と ruff: file-ignore が追加された
ruff: ignore は noqa のように同じ行の診断を抑制でき、独立したコメントとして書けば次の論理行全体に適用できる
- 複数行で書かれた関数ヘッダーでは、
def からコロンまでが1つの論理行として扱われる
ruff: file-ignore は ruff: noqa のように、ファイル全体で指定した診断を抑制する
- 各抑制コメントには、ルールコードの後ろに 適用理由 を書ける
--add-ignore CLIオプションは、必要な ruff: ignore コメントを自動で追加する
- プレビューモードでは、
F401 のようなコードの代わりに unused-import のような ルール名 も使える
- コメント仕様の全体は Ruff linterドキュメント にまとまっている
修正diffと出力形式
check と format は以前から --diff をサポートしていたが、通常の診断とは別動作だったため、修正理由を示す診断と一緒には表示されなかった
- v0.16のデフォルト
full 出力は、可能なリンター・フォーマッターの修正内容を 診断の下にdiffで表示 する
format --check もリンターがサポートする全出力形式を利用できる
- 機械可読なJSONを生成できる
- GitHubやGitLabがCIでコメントとしてレンダリングする形式を出力できる
- サポート形式はCLIヘルプと 出力形式ドキュメント で確認できる
互換性と安定化
- v0.16の破壊的変更は少数のため、多くの場合はコードや設定を大きく変えずに更新できる
- JSON出力の
filename、location、end_location、fix.edits[].location、fix.edits[].end_location は、空文字列や1行1列をデフォルト値として使う代わりに null になる可能性がある
- 現時点で影響を受ける診断はごく少ないが、今後のルールではより一般的になる可能性がある
- 12件のルールがプレビューから安定状態に移行した
- Airflow 3関数シグネチャ互換性
AIR303、著作権表示 CPY001、float変換 FURB164、ソート済みmin/max FURB192
- コレクションリテラルの文字列結合
ISC004、例外ハンドラー外での例外ロギング LOG004、不正なbool戻り値型 PLE0304
- 過剰な位置引数
PLR0917、StopIterationの返却 PLR1708、Union内の None 位置 RUF036
- クラス辞書のannotationアクセス
RUF063、__all__ の重複項目 RUF068
- 一部の既存ルールの 安定化された動作 もデフォルトで適用される
BLE001 は critical、error、exception 以外の logging メソッドで例外を記録しても抑制される
FA102 は collections.abc など追加のPEP 585互換APIを検査する
INT001・INT002・INT003 は、gettext を builtins._ に代入するような一般的な使用方法も検査する
S310 はローカル文字列リテラルのバインディングを解決し、誤検知を減らす
S508・S509 は最新のPySNMP推奨APIをサポートする
UP019 は typing.Text だけでなく typing_extensions.Text も認識する
- 変更点の全体は GitHubリリース で確認できる
1件のコメント
Hacker Newsのコメント
約3,000行規模のPythonプロジェクトをv0.15.xから新バージョンに上げたが、時間はあまりかからず、以前のバージョンが見逃していた問題を多数見つけて、コード品質も向上した
提案に従った手動修正: https://github.com/nickjj/plutus/commit/9af66d31f98bef841588...
行長ルールの再有効化: https://github.com/nickjj/plutus/commit/21789f89bbcee37913c1...
未使用変数への
_接頭辞の強制: https://github.com/nickjj/plutus/commit/a272c77b932e1c78558a...Ruffの自動修正: https://github.com/nickjj/plutus/commit/6fe69cf88385ebdf9b8c...
AstralがOpenAIに買収された後も、Ruff、ty、uvが活発に開発されていてうれしい
uvとRuffは素晴らしく、tyもいつかその水準に到達してほしい
互いに恣意的なルールを実装し、何が良いPythonコードかですら合意できていない文法警察ツールに、これほど熱狂しているのが驚きだ
複数行の辞書を1行にまとめてコメントの意図を壊したり、空白2つやダブルクォートのような些細な書式だけを直したりする。実際の問題は行末の空白やimportの並び替えではなく、解釈しづらい10行のリスト内包表記なのに、こうしたツールでは捕まえられない
職場ではpylint、flake8、black、Ruffを使っていて、変更のたびに何百ものコミットが生まれたが、そのエネルギーは他に使ったほうがよかったはずだ
自動実行の結果をそのまま受け入れればlint議論から抜けられるが、こうしたツールがない組織では、書式を考えたり議論したりするのに実際に時間を使っていた
チーム開発では個人的な好みを強く押し通すより、周囲の意見を聞き、協業と職人的姿勢の優先順位を見直す必要がある
行コメントがあるのに行をまとめる動作は不適切に見える。ダブルクォートはPythonでは単なるスタイルの選択であり、シングルクォート内にダブルクォートがあればRuffもそのままにする
GoにもRuffのようなツールがあればよいのにと思う。複数の言語で優れたツールが出ているが、Goエコシステムは分散していて、Ruff、Oxc、Biome、Magoほど完成度が高いと感じられるツールがない
比較的新しくてあまり知られていないが、go fixとgo vetの基盤であり、Goチームはモジュール作者がgo fix実行時に自動で動くカスタム分析パスを簡単に定義できるよう取り組んでいるようだ
analysis.Analyzer構造体でAST、型、SSA情報にアクセスでき、アナライザ間で情報を組み合わせることもできる。これをバイナリにコンパイルしてgo fixに渡せば、ツールチェーンが複雑なキャッシュまで処理してくれる。Goチームが直接作りツールチェーンに組み込んでいるため、golangci-lintのようなツールも長期的にはこのフレームワークに統合される可能性が高いAIエージェントにGo Analysisアナライザを書かせてgo fixで動かすよう指示することもでき、私のプロジェクトでも不正確なMarkdown指示の代わりに、複数のルールを決定論的に自動強制するため活用している
gofmt**を望んでいた。Ruffはフォーマッタではなくリンターだが、最近のPythonエコシステムの発展方向は歓迎したい公式ツールが特定のスタイルを強制しないPythonやTypeScriptとは違い、GoではRuffやBiomeを初めて使ったときのような劇的な効果は出にくい
デフォルトで413個のルールを有効化すれば、多くのプロジェクトで設定をいじらなくても有用なlintを受けられるので、よい変化だと思う
最近はエージェントに、すべてのlint警告を決められた基準に従って修正するよう任せて数時間置いておけば解決できるかもしれないが、Ruffが提供する細かな診断自体は歓迎したい
Ruffにも、適用するデフォルトの集合を決めるNixのstateVersionのような機能が必要だ。複数のリポジトリでRuffを更新すると、新しいデフォルトルールが追加されるたびにすぐ無効化するか違反を修正しなければならず、結果を予測しにくい。
有効化するルールをすべて許可リストに書くこともできるが、設定はシンプルに保ち、みんなが数時間投資できるときにstate versionだけ上げるほうがよい
pyproject.tomlで望むRuffのバージョンを固定する方式のほうが適切だ。各プロジェクトが準備できたときにバージョンを上げればよいので、複数プロジェクトを同時に調整する必要がなく、1つが遅れても残りが足止めされないhttps://github.com/astral-sh/ruff/releases/tag/v0.1.0
エージェントコーディングの時代には強力なlintingがこれまで以上に重要で、より多くの言語でforbidigoのようなツールを見たい
コーディングエージェントも些細な問題を直すのに多くのトークンを使ったり、テストが失敗すると完全に無効化したりする。AIの結果の全体的な正確さは信頼するようになったが、コード品質に対する判断力はまだ信じがたい
ルールが413個もあるのに、新しいコードベースに加わるたびにimportの並び替えをめぐって同じ3つの議論を繰り返すことになる
いまや設定不要での利用が推奨されているようでうれしい。新しい
.ruff.tomlにはline-length = 300だけ置けばよい