1
/
5

Pythonの静的コード解析ツール「Ruff」

はじめに

こんにちは、アポロ株式会社でデータサイエンティストをしている張と申します。
プロジェクトにおいて、ソースコードの品質、可読性、そして開発効率を向上させるために、PythonのLinterを使っています。
今回は、現在比較的新しいPythonのLinter「Ruff」についてご紹介いたします。

Linterの概要

まずは簡単にLinterを紹介したいと思います。
Linterは静的コード解析ツールです。プログラムコードのチェックツールであり、文法エラー、コーディングスタイルの問題、潜在的な脆弱性を検出し、提案や修正方法を提供します。Linterツールを使用することで、開発者はプログラムコードのエラーを素早く発見して修正し、コードの品質を向上させることができます。
Pythonでよく使われるリンターツールには、Flake8、Pylintなどがあります。Linterは、現代のソフトウェア開発において必要不可欠な要素となっており、もしまだLinterを使用したことがなければ、さっそく今日から使い始めましょう!

Ruffとは?

Ruffは2022年8月にリリースされ、Rust言語で開発された高性能なPythonのLinterです。

Rustの概要

Rustは、Webブラウザソフトウェア「Firefox」を開発しているMozillaが支援するオープンソースのプログラミング言語です。安全性、並行性、効率性がRustの特長です。
最近では、Rustはフロントエンドのツールチェーン領域でますます影響力を持ち、さらには「RustはJavaScriptインフラストラクチャの未来である」[1]という主張もされています。Rustで再実装できるフロントエンドツールは、全てRustで書き直され、その結果、1つの特徴が際立っています。それが「速さ」です!

Ruffの特徴

Ruffの特徴としては主に二つがあります。速度とサポートするルールです。

速度

Rust言語で開発されたPython Linter Ruffは他のlinterよりも約10〜100倍速いと言われており、キャッシュ、自動修正、Python 3.11のサポート、さらにはpre-commitやVS Codeの拡張機能などの機能を備えています。使い心地も良く、機能はFlake8に近い完全な機能を持っています(詳細は参考文献[2]を参照)。
そのため、多くの人々はRuffを開発時のLinterとして選び、FastAPI、pandas、Apache Airflowなどの開発チームもRuffをLinterとして採用しています[3]。

Linting the CPython codebase from scratch.

サポートするルール

Ruff は、Flake8、autoflake、isort、pyupgrade、yesqa などの人気ツールを参考にしており、それらを基に500以上のルールをRustで再実装しました。Ruff 自体はプラグインをサポートしていませんが、数十の一般的なFlake8プラグインの設計を取り込んでおり、組み込まれているルールの範囲は他のどのツールよりも広いです。

Ruffのインストールと設定方法

これからはコマンドによる実行方法とVisual Studio Code (VSCode)向けの設定方法を紹介します。

コマンドによる実行方法

  1. まずはRuffをインストールしましょう。
    pip install ruff
  2. プロジェクトのフォルダに以下のようなファイルpyproject.tomlを作成しましょう。
pyproject.toml


[tool.ruff]
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
select = [
"ALL",
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"C901", # too complex
"B904", # raise from err
]
fixable = ["ALL"]
target-version = "py39"

# Exclude a variety of commonly ignored directories.
exclude = [".venv", "venv"]pyproject.toml


[tool.ruff]
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
select = [
"ALL",
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"C901", # too complex
"B904", # raise from err
]
fixable = ["ALL"]
target-version = "py39"

# Exclude a variety of commonly ignored directories.
exclude = [".venv", "venv"]

select=[]の中には守りたいルールを入れます。全部のルールを守る場合はselect=["ALL"]で表現します。特に気にしないルールはignore=[]の中に入れます。
守りたいルールと気にしないルールはチーム内で話し合って決めましょう。
ルールの詳細な内容は参考文献[4]をご参照ください。
exclude=[]の中には、Ruffが検査しないディレクトリが含まれています。

3.ルールを設定しましたら、Ruffを実行するために以下のテスト用コードを作成しましょう。

test_ruff.py


import os
from pathlib import Path
import sys

print("hello world!");
x = Path()
y = sys.argv
# z = os.environ


def test(a,b,c):
return a+btest_ruff.py


import os
from pathlib import Path
import sys

print("hello world!");
x = Path()
y = sys.argv
# z = os.environ


def test(a,b,c):
return a+b

4.ファイルtest_ruff.pyのディレクトリに以下のコマンドを実行します
ruff check .
実行した結果は以下になります。

src/test_ruff.py:1:1: INP001 File src/test_ruff.py is part of an implicit namespace package. Add an __init__.py.
src/test_ruff.py:1:1: D100 Missing docstring in public module
src/test_ruff.py:1:1: I001 [] Import block is un-sorted or un-formatted
src/test_ruff.py:1:8: F401 [] os imported but unused
src/test_ruff.py:5:1: T201 print found
src/test_ruff.py:5:22: E703 [] Statement ends with an unnecessary semicolon
src/test_ruff.py:8:1: ERA001 [] Found commented-out code
src/test_ruff.py:11:5: ANN201 Missing return type annotation for public function test
src/test_ruff.py:11:5: D103 Missing docstring in public function
src/test_ruff.py:11:10: ANN001 Missing type annotation for function argument a
src/test_ruff.py:11:12: ANN001 Missing type annotation for function argument b
src/test_ruff.py:11:14: ARG001 Unused function argument: c
src/test_ruff.py:11:14: ANN001 Missing type annotation for function argument c
src/test_ruff.py:12:15: W292 [*] No newline at end of filesrc/test_ruff.py:1:1: INP001 File src/test_ruff.py is part of an implicit namespace package. Add an __init__.py.
src/test_ruff.py:1:1: D100 Missing docstring in public module
src/test_ruff.py:1:1: I001 [] Import block is un-sorted or un-formatted
src/test_ruff.py:1:8: F401 [] os imported but unused
src/test_ruff.py:5:1: T201 print found
src/test_ruff.py:5:22: E703 [] Statement ends with an unnecessary semicolon
src/test_ruff.py:8:1: ERA001 [] Found commented-out code
src/test_ruff.py:11:5: ANN201 Missing return type annotation for public function test
src/test_ruff.py:11:5: D103 Missing docstring in public function
src/test_ruff.py:11:10: ANN001 Missing type annotation for function argument a
src/test_ruff.py:11:12: ANN001 Missing type annotation for function argument b
src/test_ruff.py:11:14: ARG001 Unused function argument: c
src/test_ruff.py:11:14: ANN001 Missing type annotation for function argument c
src/test_ruff.py:12:15: W292 [*] No newline at end of file

5.以下のコマンドを実行して、上記に[*]が付いている内容を自動修正してくれます。
ruff check . --fix

6.修正済の内容は以下になります。

test_ruff.py


import sys
from pathlib import Path

print("hello world!")
x = Path()
y = sys.argv

def test(a,b,c):
return a+btest_ruff.py


import sys
from pathlib import Path

print("hello world!")
x = Path()
y = sys.argv

def test(a,b,c):
return a+b

importの順番や使わないimportについてを修正してくれました。
ほかのフォーマット(演算子と変数の間に空白がないなど)を気になりましたら、自動的に整形してくれるツールblackを利用しても大丈夫です。

Visual Studio Code (VSCode)向けの設定方法

VSCodeの拡張機能を利用する場合は以下になります。

  1. まずは以下の拡張機能をインストールします:
    Ruff v2023.30.0 Astral Software

2.インストールが完了したら、検査結果は「PROBLEMS」というタブで表示されます。

3.自動修正をしたい場合は以下の設定をします。

  • 以下のようにsettings.jsonを開きます(control+shift+pで以下の項目を選択します)。
  • ファイルにsettings.json以下の内容を貼り付けます。
settings.json


{
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
}settings.json


{
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
}

こうしましたらファイルを保存する際に自動的に修正してくれます。
コマンドの利用でもVS Codeでも、ルールは同じ.tomlファイルに書きます。
コマンドでもVSCodeでも、設定するルールは同じ.tomlファイルに書きます。

【補足】
VSCodeでblackを利用する場合は以下の拡張機能をインストールします:

  • Black Formatter v2022.6.0 Preview Microsoft

settings.jsonに以下の内容を設定します。

settings.json


{
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"editor.defaultFormatter": "ms-python.black-formatter",
},
"editor.formatOnSave": true,
}settings.json


{
"[python]": {
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"editor.defaultFormatter": "ms-python.black-formatter",
},
"editor.formatOnSave": true,
}

まとめ

今回はPython のLinter Ruffの概要とインストール、設定方法を紹介しました。
チームメンバーたちが同じLinterとルールを使うことで、コードの一貫性を保つことができて、品質の高いコードを維持することができます。また、便利な機能を活用して、自動修正や検査結果を確認することで、バグやエラーを早めに発見することも可能です。ぜひ、速さと広範なルールが特徴のRuffを使ってみて、プログラミングの効率も向上させてみましょう!

参考文献

[1] Rust Is The Future of JavaScript Infrastructure
[2] How does Ruff compare to Flake8?
[3] Ruff doc
[4] Ruff rules

最後まで読んでいただき、ありがとうございます。
アポロならではの技術的課題に対する取り組みやプロダクト開発の試行錯誤で得た学びなどを定期的に発信していきます。少しでも業界へ貢献できれば嬉しいです。
今後ともよろしくお願いいたします。
アポロでは、一緒に働く仲間を募集中です。 興味のある方は、ぜひ下記の採用サイトをご覧ください。

アポロ株式会社からお誘い
この話題に共感したら、メンバーと話してみませんか?
アポロ株式会社では一緒に働く仲間を募集しています

同じタグの記事

今週のランキング

張 邑さんにいいねを伝えよう
張 邑さんや会社があなたに興味を持つかも