Rust製の高速Linter・Formatter「Ruff」の初心者向け解説
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の使い方を紹介していきたいと思います。本記事では、なるべく特定のツールに依存しないシンプルな例を記載します。
実際に個人・プロジェクトで使用するときは、環境に合わせた導入を実施してください。公式ドキュメントには、複数の環境での環境構築の例が載っているので参考になると思います。
まずは基本となる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エディタ入門」を参照ください。
本記事では、Ruffの設定にフォーカスして説明します。Ruffの拡張機能は、拡張機能でRuffを検索して出てくる、Ruffをインストールしてください(下図の一番上の項目です)。
続いて、設定画面で「Ruff」で検索して、Ruff:Line Length
に前述した文字数制限(127)を設定します。
古いLintのArgs設定
以下は古いRuffの設定方法です。参考までに残しておきますが、新しい方法で設定してください。
設定画面で「Ruff」で検索して、Ruff > Lint: Args
とRuff > Format: Args
にオプションを設定します。今回は、前述した文字数制限のみを設定します。
sample.py
を開くとLintのチェックでの問題点にアンダーラインが表示されます。
問題点(アンダーラインがある箇所)にカーソルを当てると、以下のように問題となる理由などが表示されます。
VS Codeエディタ上で、Lintエラーの修正、Formatをする場合は、F1キーでコマンドパレットを開いて「Ruff」で検索して、Ruff: Fix all auto-fixable problems
、Ruff: Format document
を選択することで実現できます。
手動でFormatをするのが手間な場合は、設定画面でEditor: Default Formatter
をRuff
に設定してEditor: Format On Save
にチェックを入れることで、自動Formatが設定できます。以下が設定画面です。この設定により、ファイルをセーブするたびに自動でFormatがかかります。お好みで設定してください。
VS CodeエディタでのRuffの設定は、他にも多数あるので、より深く知りたい方は、公式ドキュメントなどを参考に使いやすい設定を探してみてください。
今回は、初心者向けにGUIでの設定方法を紹介しましたが、JSONでの設定に慣れている人は、以下記事も参考になると思います。
GitHub ActionsへのRuffの設定
GitHub ActionsのワークフローにRuffのLintチェックとFormatチェックを設定することで、コードに変更があるたびに、自動でのチェックが可能となります。GitHub ActionsとGitHubの適切な設定と運用をすれば、個人のうっかりミスによるチェック漏れを防ぐことができます。
GitHub Actionsのワークフローの設定含めたGitHubのリポジトリをサンプルとして準備しました。
ワークフローを設定したファイルは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を試してみたり、プロジェクトへの導入を検討していただけましたら幸いです。
参考リンク
変更履歴
- 2025/02/11 文字数制限の指定方法をRuffの仕様変更に合わせて修正
Discussion