Skip to content

Ruffでのインポートソート

問題の根本原因

Ruffのデフォルト設定でruff formatを実行してもインポートがアルファベット順にソートされない問題が発生します。具体的な動作例:

  1. 未ソートのインポートを持つファイル(例):

    python
    import os
    import collections
  2. ruff formatを実行:

    bash
    $ ruff format file.py
    1 file left unchanged  # ファイルが変更されない
  3. 一方、isortでは正常にソートされます:

    bash
    $ isort file.py  # ファイルが修正される

重要な認識誤差
Ruffではインポートのソート機能がフォーマッターではなくリンティングの範疇に属します。ruff formatはインポートの再配置を実行せず、書式の調整のみ行うため、この現象が発生します。

設計思想の背景

Ruffの設計思想は「フォーマッターがプログラムのAST(抽象構文木)を変更しない」という原則に基づいています。インポートの順序変更やグルーピングは意味論的な変更を伴うため、フォーマッターでは扱わず、リンチングステージでのみ実施されます。

正しい解決方法

基本コマンドでのソート実行

インポートソートを強制するには、以下のコマンドシーケンスを実行します:

bash
ruff check --select I --fix  # インポートのソートと修正
ruff format  # ソート後の書式整頓

設定ファイルでの恒久的対応

プロジェクト全体で常にインポートソートを適用するには、ruff.tomlまたはpyproject.tomlに以下を追加:

toml
[tool.ruff]
# Iルール(インポートソート)を必須チェック項目に追加
extend-select = ["I"]

動作の注意点

  • ruff check --select I --fixisortのデフォルト動作(profile="black")に相当
  • インポートのグループ分けやカスタマイズが必要な場合は設定オプションで調整必須

スクリプト化による効率化

package.jsonに登録して一括実行(npm環境例):

json
{
  "scripts": {
    "lint": "ruff check --select I --fix && ruff format"
  }
}

技術的補足説明

なぜフォーマッターでは扱わないのか?

Ruffは設計上、フォーマッターがコードの構造を変えないことを保証しています。インポートの並べ替えは:

  1. モジュールのインポート順序により動作が変わる可能性がある
  2. 依存関係の変更を伴う意味論的な操作になる
    → これらはリンターが扱うべき領域と判断

isortとの比較

Ruffのインポートソートはisortとの互換性を意識して設計されていますが、完全に同一ではない点に注意:

  • 類似点:デフォルトソート順序、グループ分けロジック
  • 相違点:カスタムルールの実装方法、設定オプションの詳細
    詳細は公式比較ドキュメント参照

トラブルシューティング

ソートが適用されない場合の確認事項

  1. 設定ファイルでextend-select = ["I"]が正しく宣言されているか
  2. --fixオプションを忘れていないか(修正適用なしでは変更不可)
  3. グローバル設定とローカル設定の衝突(.ruff.tomlの階層確認)

将来的な改善

現在issue#8232で、フォーマッターとリンターの統合操作に関する議論が進行中。将来的にはruff format単体でのソートが可能になる見込みです。