✔️

Rust製の高速Linter・Formatter「Ruff」の初心者向け解説

2025/01/27に公開

RuffでPythonコードをLint・Format

会社で、同僚にPythonコードのLint(コードの静的解析)、Format(コード整形)にRuffが便利と教えてもらいました。

Pythonに関しては、ずっとLintにはFlake8を使って、VS Codeの拡張機能への組み込みとGitHub ActionsでPull Requestに対しての自動チェックを実施していました(詳細は、こちらの記事を参照ください)。

Ruffに関しては、Flake8からの乗り換えコストもあるので、わざわざ新たに覚えて使う必要はないかと思っていたのですが、調べて少し使ってみるとRuffに色々とメリットがあって、実際に使ってみて便利なことが体感できました。なので「新しいプロジェクトに関してはRuffを使っていくのがオススメ」とあっさり考えが変わりました。

ただ、Ruffを使う上で、プロジェクトで最初に私含めたRuffを初めて触るメンバーがハマった点もあったので、本記事では初心者向けに、Ruffの特徴・使い方・注意点等について説明したいと思います。

Ruffに書かれた記事は既に多くありますが、本記事は主にRuffを使ったことがない人向けに、なるべく分かりやすく、最小限必要なことを記載しています。参考にしていただけましたら幸いです。

Ruffのメリット・注意点

RuffのFlake8と比較したメリットについては、大きく以下の2つがあります。

  • 高速な動作
  • LinterとFormatter両方の機能がある

高速な理由は、言語としてRustを使っている点が挙げられます。それ以外の工夫も含めた高速化の詳細はRuff はなぜ速いのか?という記事に詳しく書かれていますので、興味ある方は参照ください。

LinterとFormatterの両方の機能がある点も魅力的です。Flake8を使う場合は、他にBlackというFormatterを使うことが多いのですが、Ruffのみで2つの機能が完結するのは、VS Codeの拡張機能やGitHub Actionsへ組み込むことを考えると、手間と導入にあたっての間違いが減るのでありがたいです。

注意点としては、Flake8の完全上位互換というわけではない(参考:RuffはFlake8の全ての要素を含んでいる、のでしょうか?)ので、既存のプロジェクトで置き換えるのには注意が必要となります。

個人的には、すでにプロジェクトでLintやFormatに関して一定のルールを確立できてうまく運用できているのであれば、無理をしてFlake8、BlackからRuffに無理に乗り換える必要はないと思います。

Ruffの使い方

早速Ruffの使い方を紹介していきたいと思います。本記事では、なるべく特定のツールに依存しないシンプルな例を記載します。

実際に個人・プロジェクトで使用するときは、環境に合わせた導入を実施してください。公式ドキュメントには、複数の環境での環境構築の例が載っているので参考になると思います。

https://docs.astral.sh/ruff/

まずは基本となるRuffのコマンドベースでのLintチェック・Formatチェックの方法を説明していきます。

Ruffインストール

pipでインストールするときは以下の通りです。

$ pip install ruff==0.9.1

バージョンによって挙動が異なるので、意図しないエラーを防ぐために、特にプロジェクトなどで使うときは、バージョンを固定することをおすすめします。

特に、後述するGitHub Actionsでエラーになるときには、Ruffのバージョンは必ずチェックするようにしましょう。

Lintチェック

Lintチェックは、コードの潜在的な問題・バグ・非推奨な書き方・スタイル違反などを検出するために行います。Lintチェックをする前に、まずはLintチェックするコードを用意しましょう。わざとLintチェックにひっかかるようなPythonファイル(sample.py)を準備しました。こちらのファイルを以下のコマンドでダウンロードしてください。

$ curl -o sample.py https://raw.githubusercontent.com/karaage0703/ruff_test/main/sample.py

続いて、同じ場所で以下のコマンドを実行しましょう。

$ ruff check

ruffは、デフォルトでは自分のいるフォルダ以下、全てのPythonファイルが対象となります。対象を限定したいときはruff check sample.pyと、対象となるファイルを引数で指定します。

Lintチェックをすると、以下のように問題点が指摘されます。

sample.py:2:1: E401 [*] Multiple imports on one line
  |
1 | from typing import List,Dict
2 | import json,sys
  | ^^^^^^^^^^^^^^^ E401
3 | 
4 | def calculate_sum(numbers:List[int])->int:
  |
  = help: Split imports

sample.py:2:8: F401 [*] `json` imported but unused
  |
1 | from typing import List,Dict
2 | import json,sys
  |        ^^^^ F401
3 | 
4 | def calculate_sum(numbers:List[int])->int:
  |
  = help: Remove unused import

sample.py:2:13: F401 [*] `sys` imported but unused
  |
1 | from typing import List,Dict
2 | import json,sys
  |             ^^^ F401
3 | 
4 | def calculate_sum(numbers:List[int])->int:
  |
  = help: Remove unused import

Found 3 errors.
[*] 3 fixable with the `--fix` option.

便利ですね。RuffにLintエラーを修正してほしいときは以下のコマンドを実行します。

$ ruff check --fix

修正完了して、再度ruff checkでチェックしたら、All checks passed!と表示され、エラーがなくなったことを確認できます。

Formatチェック

Formatチェックは、コードのフォーマット (整形) を、あらかじめ定められたスタイル通りになっているかチェックします。

以下のコマンドを実行することでFormatチェックができます。

$ ruff format --check

先程のsample.pyに対して実行すると、以下のように「フォーマット(整形)が必要だよ」と表示されます。

Would reformat: sample.py
1 file would be reformatted

ただ、これだけだとどうフォーマットが必要なのか分かりません。確認したいときは、以下のように--diffオプションをつけます。

$ ruff format --check --diff 

以下のように、フォーマットされるべき箇所が表示されます。

--- sample.py
+++ sample.py
@@ -1,22 +1,27 @@
-from typing import List,Dict
-import json,sys
+from typing import List, Dict
+import json, sys
+
 
-def calculate_sum(numbers:List[int])->int:
-    total=0
+def calculate_sum(numbers: List[int]) -> int:
+    total = 0
     for num in numbers:
-        total+=num    # 不適切なスペース
+        total += num  # 不適切なスペース
     return total
 
-def process_data(data:Dict[str,any])->None:   # anyは小文字で使用されており、型アノテーションとして正しくない
-    if(data['status']=='success'):    # 不適切なスペース、シングルクォートの使用
+
+def process_data(
+    data: Dict[str, any],
+) -> None:  # anyは小文字で使用されており、型アノテーションとして正しくない
+    if data["status"] == "success":  # 不適切なスペース、シングルクォートの使用
         print("Processing successful data...")
-        values=[1,2,3,4,5]    # 不適切なスペース
-        result=calculate_sum(values)
+        values = [1, 2, 3, 4, 5]  # 不適切なスペース
+        result = calculate_sum(values)
         print(f"Result: {result}")
     else:
-        print ('Error in processing')  # 不適切なスペース、シングルクォートの使用
+        print("Error in processing")  # 不適切なスペース、シングルクォートの使用
 
-if __name__ == '__main__':    # シングルクォートの使用
-    test_data={'status': "success"}   # 不適切なスペース
+
+if __name__ == "__main__":  # シングルクォートの使用
+    test_data = {"status": "success"}  # 不適切なスペース
     example_variable = "This is an example of a line that slightly exceeds the 127-character xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx."
-    process_data(test_data)
\ No newline at end of file
+    process_data(test_data)

1 file would be reformatted

フォーマットしたいときは、以下のコマンドを実行しましょう。

$ ruff format

フォーマットが完了すると1 file reformattedと、フォーマットが完了した旨のメッセージが表示されます。

以下コマンドで再びチェックします。

$ ruff format --check sample.py

1 file already formattedと表示されて、ちゃんとフォーマットが完了していることが確認できました。

オプションに関して

Ruffのオプションに関しては、以下のヘルプコマンドやRuffのドキュメントで確認できます。

$ ruff --help
$ ruff check --help
$ ruff format --help

よく設定するのは、1行の文字数の最大値です。Pythonのコーディングルールを定めたPEP8では、1行の文字数は79文字以内と規定されています。これは古いモニタのスペックを考慮して設定された一面もあるので、現在の多くのモニタでは、もう少し大きい数の方が見やすくなることが多いため、大きな数にするケースがあります。

例えば、127文字以内に設定するためには--line-length=127とオプションをつけることで、1行の文字数の最大値を127に変更することが可能です。例えば、Lintのチェックの場合のコマンドの例は以下となります。

$ ruff check --line-length=127

Lintの修正、Formatに関してもオプションの指定方法は同様です。

その他、Ruffは、デフォルトでは全てのルールをチェックしているわけではありません。どのルールを適用するかは、オプションにより細かく設定できます。ただ、数が多いとコマンドが長くなってしまうので、ある程度数が増えたら設定ファイル(pyproject.toml等)を使うと便利です。本記事では、設定ファイルについての詳細については割愛しますので、詳しく知りたい方は、公式ドキュメントなどを参考にしてください。

全部のルールを適用したLintチェック

Ruffのデフォルトの設定は、全てのルールをチェックしているわけではないと書きました。例えば、Lintを一番厳しい条件(全てのルールを適用した状態)でチェックする場合は、以下のようにオプションを設定します。

$ ruff check --select ALL

結果は省略しますが、デフォルト設定だと3つだったエラーが、--select ALLを設定することで22まで増えました。

全部のルールを適用すると、ルールに沿ったコードにはなりますが、逆に開発しづらくなるケースもあるので、どこまでルールを適用するかは、プロジェクトの目的に応じて決めるのがよいと思います。

まずは、Ruffのデフォルト設定を使い、徐々にアップデートしていくというのも、一つの選択肢だと思います。

VS Codeエディタの設定

前述したコマンドを使うことで、RuffのLintとFormatを最低限使用することができますが、実際に開発するとき、いちいちコマンドを実行するのは面倒ですし、問題点もできればリアルタイムに分かりやすく示してもらえると便利ですね。

VS Codeエディタを使っている場合は、拡張機能を使うことでVS Code上でRuffによるリアルタイムのチェックが可能です。VS Codeエディタ自体のセットアップ、基本的な使い方、拡張機能については拙作「VS Codeエディタ入門」を参照ください。

https://zenn.dev/karaage0703/books/80b6999d429abc8051bb

本記事では、Ruffの設定にフォーカスして説明します。Ruffの拡張機能は、拡張機能でRuffを検索して出てくる、Ruffをインストールしてください(下図の一番上の項目です)。

続いて、設定画面で「Ruff」で検索して、Ruff:Line Lengthに前述した文字数制限(127)を設定します。

古いLintのArgs設定

以下は古いRuffの設定方法です。参考までに残しておきますが、新しい方法で設定してください。

設定画面で「Ruff」で検索して、Ruff > Lint: ArgsRuff > Format: Argsにオプションを設定します。今回は、前述した文字数制限のみを設定します。

sample.pyを開くとLintのチェックでの問題点にアンダーラインが表示されます。

問題点(アンダーラインがある箇所)にカーソルを当てると、以下のように問題となる理由などが表示されます。

VS Codeエディタ上で、Lintエラーの修正、Formatをする場合は、F1キーでコマンドパレットを開いて「Ruff」で検索して、Ruff: Fix all auto-fixable problemsRuff: Format documentを選択することで実現できます。

手動でFormatをするのが手間な場合は、設定画面でEditor: Default FormatterRuffに設定してEditor: Format On Saveにチェックを入れることで、自動Formatが設定できます。以下が設定画面です。この設定により、ファイルをセーブするたびに自動でFormatがかかります。お好みで設定してください。

VS CodeエディタでのRuffの設定は、他にも多数あるので、より深く知りたい方は、公式ドキュメントなどを参考に使いやすい設定を探してみてください。

今回は、初心者向けにGUIでの設定方法を紹介しましたが、JSONでの設定に慣れている人は、以下記事も参考になると思います。

https://zenn.dev/enven/articles/python-ruff-with-vscode

GitHub ActionsへのRuffの設定

GitHub ActionsのワークフローにRuffのLintチェックとFormatチェックを設定することで、コードに変更があるたびに、自動でのチェックが可能となります。GitHub ActionsとGitHubの適切な設定と運用をすれば、個人のうっかりミスによるチェック漏れを防ぐことができます。

GitHub Actionsのワークフローの設定含めたGitHubのリポジトリをサンプルとして準備しました。

https://github.com/karaage0703/ruff_test

ワークフローを設定したファイルはruff_test/.github/workflows/ruff.ymlにあります。

以下は、Ruffが設定されている箇所の抜粋です。

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install ruff==0.9.1

      - name: Run Ruff Check
        run: ruff check --line-length=127

      - name: Run Ruff Format Check
        run: ruff format --check --diff --line-length=127

コマンドのところで確認したように、Ruffのバージョン固定、オプション設定を実施しています。

事前に手元でRuffによりLintチェックとFormatチェックをすれば、GitHub Actionsでも問題なくチェックをパスするはずですが、もしチェックがNGとなった場合は以下などを実施して修正しましょう。

  • エラーになった箇所の確認
  • RuffのバージョンとLint, FormatのオプションがGitHub Actionsと手元の環境で同じか確認
  • RuffによるLintのエラー修正、Formatの再度実施

まとめ

RuffによるLint・Formatについて、主にRuffを使ったことがない人に向けて説明しました。実際に使ってみて、高速な点とLint・Formatが一つのコマンドで実施できることは、Ruffの利点と感じています。

既に、多くのメンバーがFlake8(Black)に慣れて、プロジェクトを順調に運用できていれば、わざわざRuffに変えるメリットは薄いと思いますが、新規プロジェクトなどで、新規にLinter・Formatterを設定するときに、メンバーがFlake8(Black)もRuffも両方知らない、もしくは両方とも詳しい場合は、RuffによるLint・Formatを使うのがよいと感じます。

松尾研究所では、新規プロジェクトを立ち上げるとき、すぐ新しいリポジトリの雛形が作れるように、Cookiecutterを導入しています(Cookiecutterは、ukitaさんが提案・導入してくださいました)。この雛形のLinter・Formatterのデフォルト設定としてRuffが導入されて、会社としてもRuffを推奨のツールとしています。

この記事を読んでくださった皆様も、Ruffに興味を持たれましたら、記事を参考に、実際にRuffを試してみたり、プロジェクトへの導入を検討していただけましたら幸いです。

参考リンク

https://nikkie-ftnext.hatenablog.com/entry/format-your-python-script-following-pep8

https://nikkie-ftnext.hatenablog.com/entry/is-ruff-superset-of-flake8-my-answer-is-no-202501

https://zenn.dev/enven/articles/python-ruff-with-vscode

https://www.wantedly.com/companies/wantedly/post_articles/944466

変更履歴

  • 2025/02/11 文字数制限の指定方法をRuffの仕様変更に合わせて修正
松尾研究所テックブログ

Discussion