AI

ruffの使い方完全ガイド【インストールから応用まで】

ruffの使い方完全ガイド【インストールから応用まで】

Pythonプロジェクト開発において、コードの品質維持は非常に重要です。しかし、多くの開発者が経験する共通の悩みがあります。

プロジェクトの規模が大きくなるにつれて、コードの書き方は人それぞれになりがちです。ある開発者はスペースの入れ方を好み、別の開発者はタブを好むかもしれません。変数名の付け方や、インポートの順序も統一されません。結果として、コードレビューでは本質的なロジックではなく、スタイルの指摘に多くの時間が費やされます。

「この関数は長すぎる」「未使用のインポートがある」「セミコロンが抜けている」といった指摘は日常茶飯事です。これらの指摘を手作業で修正する時間は、本来の機能開発を圧迫します。また、CI/CDパイプラインに複数のリンターやフォーマッターを組み込むと、その実行時間は膨大になり、開発者の生産性を低下させてしまいます。

もし、これらの問題を一手に解決し、しかも圧倒的な速度で実行できるツールがあったらどうでしょうか。Python開発者の世界は一変します。本記事で紹介する「ruff」は、まさにその理想を実現するツールです。


ruffとは

ruffは、Rustで書かれた超高速なPythonリンターおよびコードフォーマッターです。既存のFlake8やBlackといったツールが抱えていた「実行速度の遅さ」や「複数ツールの管理の複雑さ」という課題を根本から解決するために誕生しました。

わずか30秒でruffの概要を掴んでみましょう。

  • Rust製による驚異的な速度: Flake8やBlackと比較して、10倍から100倍もの速度で動作します。大規模なコードベースでも、瞬時にリンティングとフォーマットが完了します。
  • オールインワンツール: Flake8(とその多くのプラグイン)、Black、isort、pydocstyle、pyupgrade、autoflakeなど、複数のPython品質ツールをruff一つで代替できます。これにより、ツールのインストールや設定の管理が大幅に簡素化されます。
  • 自動修正機能: 検出された多くの問題は、--fixオプション一つで自動的に修正されます。未使用のインポートの削除や、コードスタイルの統一などが手軽に行えます。
  • pyproject.toml対応: Pythonプロジェクトの標準的な設定ファイルであるpyproject.tomlを通じて、詳細な設定が可能です。

ruffの誕生は、「Pythonのツールはもっと速く、もっとシンプルにできるはずだ」という開発者の強い思いから来ています。特に、大規模なPythonプロジェクトでは、コード品質チェックの時間が開発者のボトルネックになりがちでした。RustのパフォーマンスとPythonエコシステムへの深い理解が融合し、ruffはPython開発の新たなスタンダードを築きつつあります。


インストール方法

ruffのインストールは非常に簡単です。Pythonのパッケージマネージャーであるpipを使用します。通常はプロジェクト固有の仮想環境にインストールすることを推奨します。

仮想環境の作成と有効化

まず、プロジェクトディレクトリで仮想環境を作成し、有効化します。

python -m venv .venv

macOS/Linuxの場合:

source .venv/bin/activate

Windowsの場合:

.venv\Scripts\activate

ruffのインストール

仮想環境が有効化されたら、pipでruffをインストールします。

pip install ruff

これでruffのインストールは完了です。

バージョン確認

正しくインストールされたか確認するために、バージョン情報を表示してみましょう。

ruff --version

例えば、以下のような出力が表示されれば成功です。

ruff 0.1.x

基本的な使い方

ruffの基本的なコマンドは非常にシンプルです。最低限これだけ知っていれば、すぐにコードの品質チェックを開始できます。

ここでは、簡単なPythonファイルを作成し、ruffの動作を確認します。

example.pyという名前で以下のファイルを作成してください。

import os, sys
import math

def calculate_area(radius):
    PI = 3.14159
    return PI * radius * radius

def unused_function():
    pass

class MyClass:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

if __name__ == "__main__":
    result = calculate_area(10)
    print(f"Area: {result}")

このコードには、いくつかのリンティングエラーとフォーマットの問題が含まれています。

1. コードのリンティングを実行する (ruff .)

現在のディレクトリにあるすべてのPythonファイルをリンティングします。これは、コードの問題点を検出する最も基本的なコマンドです。

ruff .

実行すると、以下のような出力が得られるはずです(バージョンによって出力は異なる場合があります)。

example.py:1:8: E401 Multiple imports on one line
example.py:1:11: F401 `sys` imported but unused
example.py:2:8: F401 `math` imported but unused
example.py:7:5: F841 Local variable `PI` is assigned to but never used
example.py:9:5: F811 `unused_function` redefined above without previous use
example.py:9:5: F841 Local variable `unused_function` is assigned to but never used
example.py:1:1: I001 Import block is unformatted
Found 7 errors.

ruffは、ファイル名、行番号、列番号、エラーコード、エラーメッセージを分かりやすく表示します。例えばE401は「複数インポートが一行に書かれている」、F401は「インポートされたが未使用のモジュール」といった意味です。

2. コードを自動修正する (ruff check --fix .)

検出された問題の多くは、ruffが自動で修正できます。--fixオプションを付けて実行するだけです。

ruff check --fix .

このコマンドを実行しても、コンソールには何も出力されないかもしれません。これは、ruffが問題を検出し、自動で修正したことを意味します。

もう一度ruff .を実行して、エラーが減ったか確認してみましょう。

ruff .
example.py:1:1: I001 Import block is unformatted
Found 1 error.

多くのエラーが修正され、インポートの順序に関するI001エラーだけが残りました。ossysが別々の行になり、mathも削除されています。

3. コードをフォーマットする (ruff format .)

ruffには、Blackのようなコードフォーマッター機能も内蔵されています。コードスタイルを統一するために使用します。

ruff format .

このコマンドを実行すると、コードのインデントや改行、空白などがruffの規定するスタイルに自動的に修正されます。

example.pyの内容を見てみましょう。

import os
import sys
import math

def calculate_area(radius):
    PI = 3.14159
    return PI * radius * radius

def unused_function():
    pass

class MyClass:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

if __name__ == "__main__":
    result = calculate_area(10)
    print(f"Area: {result}")

空白行が増えたり、関数定義の後に空行が挿入されたりしています。

ここで、再度ruff .を実行してみます。

ruff .
example.py:1:8: F401 `sys` imported but unused
example.py:2:8: F401 `math` imported but unused
example.py:7:5: F841 Local variable `PI` is assigned to but never used
example.py:9:5: F811 `unused_function` redefined above without previous use
example.py:9:5: F841 Local variable `unused_function` is assigned to but never used
Found 5 errors.

フォーマットによってインポートブロックは修正されましたが、unused_functionの冗長な定義や、PIの未使用、sysmathの未使用インポートはフォーマッターの範囲外です。これらはリンターの役割です。

再度ruff check --fix .を実行すると、これらの問題も修正されます。

ruff check --fix .
def calculate_area(radius):
    PI = 3.14159
    return PI * radius * radius

def unused_function():
    pass

class MyClass:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

if __name__ == "__main__":
    result = calculate_area(10)
    print(f"Area: {result}")

最終的には、未使用のインポートや変数が削除され、コードが整理されました。unused_functionは修正対象外のため残ります。


便利な使い方・応用例 3選

ruffは、単にコマンドを実行するだけでなく、実際の開発ワークフローに組み込むことで真価を発揮します。ここでは、特に役立つ応用例を3つ紹介します。

1. CI/CDパイプラインへの組み込み

継続的インテグレーション(CI)環境にruffを組み込むことで、コードがリポジトリにマージされる前に品質基準を満たしているか自動的にチェックできます。これにより、常に高品質なコードベースを維持できます。

GitHub Actionsを例にとり、CIパイプラインにruffを組み込む方法を示します。

.github/workflows/lint.ymlというファイルを作成します。

name: Lint and Format Check

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.x'

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

      - name: Run Ruff Linter
        run: ruff check .

      - name: Run Ruff Formatter Check
        run: ruff format . --check

この設定では、mainブランチへのプッシュやプルリクエスト時にワークフローが実行されます。

  • ruff check .: リンティングエラーがないかチェックします。エラーがあればCIは失敗します。
  • ruff format . --check: コードがruffのフォーマット規則に従っているかチェックします。--checkオプションを付けると、実際にフォーマットは行わず、フォーマットが必要な場合にエラーを発生させます。これにより、フォーマットされていないコードがマージされるのを防ぎます。

2. pre-commitフックとの連携

pre-commitは、Gitコミット前に自動的にコード品質チェックやフォーマットを実行するためのフレームワークです。これをruffと組み合わせることで、開発者が手動でruffを実行する手間を省き、コミットされるコードが常にクリーンであることを保証できます。

まず、pre-commitをインストールします。

pip install pre-commit

次に、プロジェクトのルートディレクトリに.pre-commit-config.yamlファイルを作成します。

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.x # 最新のruffバージョンに合わせて変更してください
    hooks:
      - id: ruff
        args: [--fix, --exit-non-zero-on-fix]
      - id: ruff-format

revは、使用したいruffのバージョンに合わせて変更してください。

設定ファイルを作成したら、pre-commitフックをインストールします。

pre-commit install

これで、git commitを実行するたびに、ruffが自動的にリンティングとフォーマットを行います。

  • id: ruff: リンティングを実行します。--fixオプションにより、可能な限り自動修正を試みます。--exit-non-zero-on-fixは、修正が行われた場合にコミットを中断し、修正されたファイルをステージングするよう促します。
  • id: ruff-format: フォーマットを実行します。

もしruffがコードを修正した場合、コミットは一度中断されます。修正されたファイルをgit add .でステージングし、再度コミットを実行してください。

3. 特定のルールを無効化/有効化する

ruffは900以上のルールを持っています。プロジェクトの要件や既存のコードベースに合わせて、特定のルールを無効化したり、特定のファイルや行を無視したりする必要がある場合があります。

pyproject.tomlでの設定

プロジェクトのルートディレクトリにあるpyproject.tomlファイルで、グローバルにルールを設定できます。

# pyproject.toml
[tool.ruff]
# 有効化するルールコードのリスト
select = [
    "E",  # pycodestyle errors
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "UP", # pyupgrade
    "C4", # flake8-comprehensions
    "TID",# flake8-tidy-imports
    "PL", # pylint
    "N",  # pep8-naming
]
# 無効化するルールコードのリスト
ignore = [
    "E501", # 行の長さが長すぎる (Blackと併用する場合はBlackに任せるか、ruff formatに任せる)
    "PLR0913", # 関数の引数が多すぎる
]
# 除外するファイルやディレクトリ
exclude = [
    ".venv",
    "venv",
    "migrations",
    "docs",
]
# ターゲットとするPythonのバージョン
target-version = "py310"
# 最大行の長さ
line-length = 88
  • select: 実行したいルール群を指定します。"E"のようにカテゴリ全体を指定することも、"E402"のように個別のルールコードを指定することも可能です。
  • ignore: 特定のルールを無効化します。
  • exclude: ruffのチェック対象から外すファイルやディレクトリを指定します。
  • target-version: 対象とするPythonのバージョンを指定します。これにより、バージョン固有の最適化や構文チェックが適切に行われます。
  • line-length: コードの最大行長を指定します。

ファイルや行単位での無視

特定のファイル全体、または特定の行だけをruffのチェックから除外したい場合は、コメントを使用します。

ファイル全体を無視する:

ファイルの先頭に# ruff: noqaと記述すると、そのファイル全体がruffのチェック対象外になります。

# ruff: noqa
# このファイル全体はruffによるリンティング・フォーマットの対象外になります。

import os

def my_complex_function(arg1, arg2, arg3, arg4, arg5): # PLR0913 (too many arguments)
    pass

特定の行を無視する:

無視したい行の末尾に# noqaまたは# noqa: [ルールコード]と記述します。

import os, sys # noqa: E401

def some_function():
    print("Hello") # noqa: F841, E999 (F841は未使用変数、E999は存在しないルール)
    # 上の行はF841とE999ルールを無視します。
    # E401: Multiple imports on one line

このように、ruffは柔軟な設定オプションを提供し、様々なプロジェクトのニーズに対応できます。


他ツールとの組み合わせ

ruffは多くの既存ツールを置き換えることを目指していますが、既存のプロジェクトや特定のワークフローでは、他のツールと組み合わせて使用する必要がある場合もあります。

Blackとの共存(または置き換え)

ruffのフォーマッターはBlackと高い互換性を持っています。ruffのREADMEには「Blackとのドロップイン互換性」が謳われています。

  • ruffでBlackを完全に置き換える: 最もシンプルな方法は、Blackをアンインストールし、ruffのフォーマッター機能のみを使用することです。ruff format .コマンドでBlackと同じようなフォーマットが可能です。これにより、ツール数を減らし、実行速度を向上させられます。

  • Blackとruffを併用する: もし既存のプロジェクトでBlackが深く組み込まれていて、ruffのフォーマッターへの移行が難しい場合は、ruffのフォーマッター機能を無効にできます。pyproject.tomlで以下のように設定します。

    # pyproject.toml
    [tool.ruff]
    # ruffのリンティングルールを設定
    select = ["E", "F", "I", ...]
    ignore = ["E501"] # 行の長さはBlackに任せるため無視
    
    [tool.ruff.format]
    # ruffのフォーマッターを無効化
    disable = true
    

    この設定により、ruffはリンティングのみを行い、フォーマットはBlackに任せる形になります。しかし、ruffの高速性を最大限に活用するためには、ruffのフォーマッターへの移行を検討することをおすすめします。

MyPyなどの静的型チェックツールとの組み合わせ

ruffは主にスタイルや構文に関するリンティングを行いますが、MyPyのような静的型チェックツールは型の整合性を検証します。これらは異なる役割を持つため、補完的な関係にあります。

ruffとMyPyは互いに干渉することなく併用できます。CI/CDパイプラインやpre-commitフックで、ruffの後にMyPyを実行する構成が一般的です。

例えば、pre-commit設定にMyPyを追加する例です。

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.x
    hooks:
      - id: ruff
        args: [--fix, --exit-non-zero-on-fix]
      - id: ruff-format
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.x.x # 最新のMyPyバージョンに合わせて変更
    hooks:
      - id: mypy

この設定により、コミット前にruffがコードスタイルと基本的な問題をチェックし、その後にMyPyが型チェックを行うようになります。

エディタ統合

VS Code、PyCharm、NeoVimなど、多くの主要なエディタにはruffの統合機能が提供されています。エディタにruffを組み込むことで、コードを記述しながらリアルタイムでリンティングやフォーマットのフィードバックを受け取れます。

例えば、VS Codeの場合、公式のRuff拡張機能(ms-python.ruff)をインストールするだけで、ファイル保存時に自動的にフォーマットされたり、問題がハイライト表示されたりするようになります。これにより、開発中に小さな問題をすぐに修正でき、CIでの失敗を減らせます。


よくある設定・カスタマイズ

ruffはpyproject.tomlを介して非常に柔軟な設定が可能です。ここでは、一般的なプロジェクトでよく使われる設定項目とその例を紹介します。

1. [tool.ruff]セクション

pyproject.toml[tool.ruff]セクションで、ruffの主要な動作を設定します。

# pyproject.toml

[tool.ruff]
# ruffのルールを適用するルートディレクトリ。デフォルトはカレントディレクトリ。
# files = ["src"]

# ターゲットとするPythonのバージョン。
# これにより、特定のPythonバージョンに合わせた最適化や構文チェックが行われます。
target-version = "py311"

# 有効化するルール群(デフォルトでは多くのルールが無効化されています)
# E: pycodestyle errors (例: E501 行の長さ)
# F: pyflakes (例: F401 未使用インポート)
# I: isort (インポートのソート)
# B: flake8-bugbear (バグの可能性のあるコード)
# UP: pyupgrade (新しいPython構文へのアップグレード提案)
# C4: flake8-comprehensions (リスト内包表記などの最適化)
# TID: flake8-tidy-imports (インポートの整理)
# PL: pylint (コード品質の向上)
# N: pep8-naming (命名規則)
# W: pycodestyle warnings (例: W292 ファイルの末尾に改行がない)
select = [
    "E", "F", "I", "B", "UP", "C4", "TID", "PL", "N", "W"
]

# 無効化するルール群
# 例えば、特定のルールの厳しさを緩和したい場合に使用します。
ignore = [
    "E501",  # 行の長さが長すぎる (Blackやruff formatに任せる)
    "PLR0913", # too-many-arguments (引数が多すぎる関数)
    "PLR0911", # too-many-return-statements (return文が多すぎる関数)
    "ANN002", # missing-type-self (selfに型ヒントがない)
    "ANN003", # missing-type-cls (clsに型ヒントがない)
]

# 除外するファイルやディレクトリ(globパターンで指定)
# CI/CDのキャッシュディレクトリや、自動生成されたコードなどを指定します。
exclude = [
    ".git",
    ".venv",
    "venv",
    "__pypackages__",
    "build",
    "dist",
    "migrations",
    "docs",
    "tests", # テストコードはリンティングを緩める場合がある
]

# 最大行の長さ。Blackのデフォルトは88。
line-length = 88

# 修正可能なルールのみを対象とするかどうか。
# fixable = ["ALL"] # すべての修正可能なルールを対象
# unfixable = [] # 修正できないルールを指定

# 特定のディレクトリに対する追加設定
[tool.ruff.per-file-ignores]
# 例えば、`__init__.py`ファイルではF401 (未使用インポート) を無視する
"__init__.py" = ["F401"]
# `settings.py`ファイルではE501 (行の長さ) とD100 (モジュールdocstringがない) を無視する
"settings.py" = ["E501", "D100"]

# インポートのソートに関する設定 (isortの代替)
[tool.ruff.isort]
known-first-party = ["my_project_name"] # 自分のプロジェクトのモジュール名を指定
known-local-folder = ["src"] # ローカルフォルダを指定
force-single-line = false # 複数行インポートを強制しない

# フォーマッターに関する設定
[tool.ruff.format]
# フォーマッターを無効化する場合
# disable = true

2. [tool.ruff.lint]セクション(非推奨、[tool.ruff]に統合)

以前のバージョンではリンター設定に[tool.ruff.lint]が使われていましたが、現在は[tool.ruff]に統合されています。古い設定ファイルを参照する際は注意が必要です。

[tool.ruff.isort][tool.ruff.format]セクション

インポートのソート(isortの代替)やフォーマッター(Blackの代替)に関する詳細な設定は、それぞれ[tool.ruff.isort][tool.ruff.format]セクションで行えます。これらの設定項目は、元のツールと互換性のあるオプションが用意されています。

これらの設定をプロジェクトのpyproject.tomlに記述することで、チーム全体で一貫したコード品質基準を適用できます。


今日からできる実行プラン

ruffの導入は、Pythonプロジェクトのコード品質向上と開発効率改善に大きく貢献します。今日からでもすぐに始められる3つのステップを紹介します。

ステップ1: ruffをインストールし、既存プロジェクトで実行してみる

まずは、あなたの既存のPythonプロジェクトでruffを試してみましょう。

  1. 仮想環境の作成と有効化: プロジェクトのルートディレクトリで仮想環境を作成し、有効化します。

    python -m venv .venv
    source .venv/bin/activate # macOS/Linux
    .venv\Scripts\activate    # Windows
    
  2. ruffのインストール: 仮想環境にruffをインストールします。

    pip install ruff
    
  3. 現状の把握: プロジェクト全体に対してリンティングを実行し、ruffがどのような問題を検出するか確認します。

    ruff .
    

    大量のエラーが表示されるかもしれませんが、心配いりません。これが現在のコードベースの品質状態です。

ステップ2: pyproject.tomlで基本的な設定を行う

検出されたエラーの多さに圧倒されるかもしれません。しかし、すべてのルールを一度に適用する必要はありません。pyproject.tomlでプロジェクトに合わせた基本的な設定を行いましょう。

  1. pyproject.tomlの作成: プロジェクトのルートディレクトリにpyproject.tomlファイルを作成します(既にある場合は追記)。

  2. 初期設定: まず、Pythonのターゲットバージョンと、最低限適用したいルールを設定します。例えば、未使用インポートの削除や基本的なスタイルの統一から始めると良いでしょう。

    # pyproject.toml
    [tool.ruff]
    target-version = "py310" # あなたのプロジェクトのPythonバージョンに合わせてください
    select = ["E", "F", "I"] # pycodestyle errors, pyflakes, isortのルールを有効化
    ignore = ["E501"]        # 行の長さ (88文字以上) は一旦無視する
    line-length = 88         # Blackのデフォルト行長に合わせる
    
  3. 自動修正を試す: 設定を適用した状態で、自動修正を試します。

    ruff check --fix .
    ruff format .
    

    これにより、多くの問題が自動的に解決され、コードが整理されるはずです。

ステップ3: CI/CDやpre-commitフックに組み込み、徐々にルールを強化する

基本的な設定でコードが整理されたら、開発ワークフローにruffを組み込み、継続的にコード品質を維持する体制を構築します。

  1. CI/CDパイプラインへの組み込み: GitHub ActionsやGitLab CIなど、あなたが使用しているCI/CDツールに、本記事で紹介した設定例を参考にruffのチェックステップを追加します。これにより、マージされるコードが常に品質基準を満たすようになります。

  2. pre-commitフックへの組み込み: 開発者がコミットする前に自動でruffを実行するよう、pre-commitツールを設定します。これにより、コードレビューでのスタイルの指摘が減り、開発サイクルがスムーズになります。

  3. ルールの段階的な強化: 最初は基本的なルールから始め、プロジェクトの状況を見ながら、徐々にpyproject.tomlselectに新しいルールを追加したり、ignoreからルールを削除したりして、コード品質基準を強化していきます。例えば、flake8-bugbear (B) やpyupgrade (UP) のルールを少しずつ導入していくと良いでしょう。

この3ステップで、あなたのPythonプロジェクトはruffの恩恵を最大限に受け、より高品質で保守しやすいコードベースへと進化していくでしょう。高速なリンティングとフォーマットが、あなたの開発体験を劇的に改善します。


参考文献

広告

-AI