Emacs から Ruff を使う

2023年4月29日 2023年5月1日

この記事では Emacs から Ruff を使う方法を紹介します。

Ruff

Ruff とは

Ruff は Rust で書かれた高速な Python の Linter です。Flake8 や isort を置き換えることができます。 Ruff の詳しい紹介は、検索していただくと素晴しい記事がたくさんありますので、そちらをご覧いただくのが良いかと思います。

以下では、インストールと設定を簡単に紹介します。

Ruff のインストール

Ruff のインストール方法は公式ドキュメントにあるように pipを始めとし、様々なインストール方法があります。 私は、以前ブログ書いたように、pipx で pip でインストールできるコマンド群を管理しているので、以下のようにインストールします。

pipx install ruff

# 公式ドキュメント通り、pip でインストールするなら
# pip install ruff

Ruff の設定

Ruff は500以上のlintルールをサポートしており、isort や Flake8 等のルールも Ruff で再実装されています。また、llintルールの中には自動修正可能なものもあります。

公式ドキュメントによると、デフォルトでは、Flake8 の E および F ルールを有効しており、使い始めたばかりであれば、デフォルトのルールセットが最適とのことです。

私はどんなルールがあるのか知りたいので、基本的にはすべてのルールを有効化して、適用しないルールを都度追加するようにしています。

lintルールの設定ファイル

Ruff は、pyproject.toml、ruff.toml、または .ruff.toml ファイルで設定します。 設定ファイルが色々なディレクトリ階層にある場合は、Ruff を適用するPythonファイルに対して、ディレクトリ階層の中で「最も近い」設定ファイルが参照されます(pyproject.toml は Ruff の設定がなければ無視されます)。

ディレクトリ階層内に設定ファイルが見つからない場合、Ruff はデフォルトの設定を使用し、ユーザ固有の設定ファイルが${config_dir}/ruff内に存在する場合、そのファイルがデフォルト設定の代わりに使用されます。${config_dir}ここに記載のあるようにOSによって異なりますので、自身の OS に従って適切に配置します。

詳しい設定ファイルの参照ルールは公式ドキュメントのpyprojecttoml discoveryをご参照ください。

私は、どのプロジェクトでもユーザ固有の設定が適用されれば良いので、以下のように設定しています。

~/.config/ruff/ruff.toml(linuxの場合)
# https://beta.ruff.rs/docs/rules/
# すべてのルールを適用
select = ["ALL"]
# 無視するルール
ignore = ["E501", "PGH", "DJ", "D", "T20", "ERA"]
# 自動修正するルール
fixable = ["I", "SIM118", "F401"]
# 自動修正するルールの中でも自動修正しない個別ルール
unfixable = []

# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]

これで Ruff の設定は完了です。以下のような形でターミナルから Ruff を実行できます。

# lintエラーの表示
ruff check test.py

# 自動修正可能なlintエラーの修正
ruff check --fix test.py

Emacs での Ruff の利用

このようにとても便利な Ruff ですが、もちろん Emacs からも使いたいです。

私が Ruff を使って、Emacs でやりたいことは以下の2つです。

  • 現在のバッファのlintエラーをリアルタイムで表示
  • 現在のバッファのlintエラーを自動的に修正

では、一つずつやっていきましょう。

現在のバッファのlintエラーをリアルタイムでバッファに表示

Ruff のlintエラーを Emacs でリアルタイムに表示するパッケージは いくつかありますが、私は flymake を利用しているので、flymake-ruffを使います。

これにより知らない記法がlintエラーとして表示されるので、とても勉強になり便利です。

(use-package flymake-ruff
  :ensure t
  :hook ((python-mode . (lambda ()
                          (flymake-ruff-load)
                          (flymake-mode t)))))

現在のバッファのlintエラーを自動的に修正

import順の修正や使っていないライブラリのimport文削除等、自明なlintエラーは自動的に修正したいので、以下の関数を Emacs の設定ファイルに追記します。

(defun ruff-fix-buffer ()
  "Use ruff to fix lint violations in the current buffer."
  (interactive)
  (let* ((temporary-file-directory (if (buffer-file-name)
                                       (file-name-directory (buffer-file-name))
                                     temporary-file-directory))
         (temporary-file-name-suffix (format "--%s" (if (buffer-file-name)
                                                                 (file-name-nondirectory (buffer-file-name))
                                                                "")))
         (temp-file (make-temp-file "temp-ruff-" nil temporary-file-name-suffix))
         (current-point (point)))
    (write-region (point-min) (point-max) temp-file nil)
    (shell-command-to-string (format "ruff check --fix %s" temp-file))
    (erase-buffer)
    (insert-file-contents temp-file)
    (delete-file temp-file)
    (goto-char current-point)))

このruff-fix-buffer関数を呼び出すことで、現在のバッファのlintエラーが修正されます。

また、以下の関数を定義して、before-save-hook に追加することで、バッファを保存する前に自動的にlintエラーを修正できるようにします。 なお、この関数は、major-mode が python-mode または python-ts-mode の場合にのみ実行できます。

(defun ruff-fix-before-save ()
  (interactive)
  (when (memq major-mode '(python-mode python-ts-mode))
    (ruff-fix-buffer)))

(add-hook 'before-save-hook 'ruff-fix-before-save)

上記の関数群は、ruff-fix.el として、パッケージにまとめているので、よろしければ使ってみてください。

終わりに

ここまで、読んでいただき、ありがとうございます。 これで Emacs から Ruff を使う環境を構築できました。

これから Ruff を Emacs で積極的に使っていきたいと思います。

Posted by mako
関連記事
コメント
...