🐍
最近話題になっているPythonリンター兼フォーマッターのRuffを試してみた
Ruffとは
- 2022年8月にリリースされた、Rust言語で書かれたPythonのリンター兼フォーマッター。
- 既存のリンターとフォーマッター(Flake8やBlackなど)に比べて数十倍速い。
- Pytorch、FastAPI、Amazonを始めとする数多くの有名なOSSや企業が導入。
- リリースから1年半弱で21k以上のスター⭐を獲得。
なぜ流行っているか
主に以下の3点が大きいでしょう。
- 圧倒的な速さ
- さまざまなツールを1つにまとめること
- 設定はpyproject.tomlだけで済む
Ruff vs. Flake8 + isort + Black
これまでPythonの静的解析や自動整形にはFlake8、isort、Blackを使ってきましたが、これらをRuffに置き換えられるかどうかを調査しながら試してみました。
コンフィグ
現在利用しているFlake8、isort、Blackのコンフィグと、それと同等に動作するRuffのコンフィグは以下の通りです。
Flake8 + isort + Black
.flake8
[flake8]
max-line-length = 119
extend-ignore = E203
exclude =
.git,
.mypy_cache,
.venv
pyproject.toml
[tool.isort]
profile = "black"
line_length = 119
[tool.black]
line-length = 119
target-version = ["py311"]
Ruff
pyproject.toml
[tool.ruff]
target-version = "py311"
line-length = 119
exclude = [".mypy_cache"]
[tool.ruff.lint]
select = ["E", "W", "F", "I", "C90"]
ignore = ["E203"]
実際に使ってみよう
Sample Code
少し雑な例ですが、いくつかの問題点があるコードを用意しました。
(テストをやりやすくするため、line-lengthを50にしています。)
t.py
import sys
from typing import List
import os
import csv
def sum_even_numbers(numbers: List[int]) -> int:
"""Given a list of integers, return the sum of all even numbers in the list."""
return sum(
num for num in numbers if num % 2 == 0
)
def show_exec_info():
"""Show execution info"""
print(os.path.abspath(__file__))
print(sys.path)
リンター
Flake8 + isort
> .venv/bin/isort t.py --check --diff
ERROR: /workspaces/workflow-hub-api/t.py Imports are incorrectly sorted and/or formatted.
--- /workspaces/workflow-hub-api/t.py:before 2023-12-25 14:50:22.213168
+++ /workspaces/workflow-hub-api/t.py:after 2023-12-25 14:50:46.607020
@@ -1,7 +1,8 @@
+import csv
+import os
import sys
from typing import List
-import os
-import csv
+
> flake8 t.py
t.py:4:1: F401 'csv' imported but unused
t.py:6:1: E302 expected 2 blank lines, found 1
t.py:7:51: E501 line too long (83 > 50 characters)
t.py:11:1: E302 expected 2 blank lines, found 0
t.py:15:1: W293 blank line contains whitespace
t.py:15:2: W292 no newline at end of file
Ruff
> ruff check t.py
t.py:1:1: I001 [*] Import block is un-sorted or un-formatted
t.py:4:8: F401 [*] `csv` imported but unused
t.py:7:51: E501 Line too long (83 > 50)
t.py:15:1: W293 [*] Blank line contains whitespace
t.py:15:2: W292 [*] No newline at end of file
Found 5 errors.
[*] 4 fixable with the `--fix` option.
フォマッター
isort + Black
> black t.py
reformatted t.py
All done!
1 file reformatted.
> isort t.py
Fixing /workspaces/workflow-hub-api/t.py
整形後のコード
t.py
import csv
import os
import sys
from typing import List
def sum_even_numbers(numbers: List[int]) -> int:
"""Given a list of integers, return the sum of all even numbers in the list."""
return sum(
num for num in numbers if num % 2 == 0
)
def show_exec_info():
"""Show execution info"""
print(os.path.abspath(__file__))
print(sys.path)
Ruff
> ruff check t.py --fix
t.py:7:51: E501 Line too long (83 > 50)
Found 4 errors (3 fixed, 1 remaining).
> ruff format t.py
1 file reformatted
整形後のコード
t.py
import os
import sys
from typing import List
def sum_even_numbers(numbers: List[int]) -> int:
"""Given a list of integers, return the sum of all even numbers in the list."""
return sum(
num for num in numbers if num % 2 == 0
)
def show_exec_info():
"""Show execution info"""
print(os.path.abspath(__file__))
print(sys.path)
結果の比較
- 多くのルール違反の表示には問題がありませんでした。
- ただし、関数定義の間に2行の空白行を挿入すべきルール(E302)の違反は、Ruffではリンターで表示されませんでしたが、フォーマッターで修正してくれました。
- isortと比較して、Ruffは使用していないライブラリを勝手に削除してくれました。
考察
上記のE302ルールを表示できなかった問題について、もう少し詳しく調査しました。
Sample Code
t.py
def func1():
pass
def func2():
pass
Flake8
> flake8 t.py
t.py:3:1: E302 expected 2 blank lines, found 0
Ruff
> ruff check t.py
前述の例と同様に、Flake8はルール違反を表示できましたが、Ruffでは表示できませんでした。 調査したところ、各ツールがサポートするルールは以下の通りです。
- Flake8がサポートするルール:https://www.flake8rules.com/
- isortがサポートするルール:https://pypi.org/project/flake8-isort/
- Ruffがサポートするルール: https://docs.astral.sh/ruff/rules/
したがって、E3シリーズなど、RuffはFlake8とisortのすべてのルールをカバーしていないことがわかります。
ただし、この問題はすでに解決途中のようです。
まとめ・感想
- 多くのツールを1つにまとめることで、手間が大幅に省けます。
- 基本的な機能についてはすでに十分と言えますが、現時点では一部のルールがサポートされていないため、Flake8 + isort + Blackを完全にRuffに置き換えるわけではありません。
- コードベースの規模が大きくない場合、性能の向上はあまり感じられないかもしれません。
- 昨年リリースされたばかりでコミュニティは活発ですが、Issueを見るとバグも少なくないです。
- OSSの成長スピードはかなり速いので、これからの展開に期待します。
Discussion